From f5d9eb0f2ecfdf839458c27f8843d65d0520bdde Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Mon, 13 Jul 2026 14:16:19 +1000 Subject: [PATCH] MDEV-39813 ST_GeomFromGeoJSON does not control recursion depth Using stack_p wasn't a portable concept in 12.3 when JSON parsing got unlimited depth. To let ST_GeomFromGeoJSON as a recursive function, needed because object order of "type" may be after the "geometries", some stack checking was required. Use the check_stack_depth function to allow excessively deep GeoJSON objects to error. --- mysql-test/main/disabled.def | 1 - mysql-test/main/gis-json.result | 5 +---- mysql-test/main/gis-json.test | 3 +++ sql/spatial.cc | 9 ++++----- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/mysql-test/main/disabled.def b/mysql-test/main/disabled.def index ff0f3ab1781c4..a38906361c562 100644 --- a/mysql-test/main/disabled.def +++ b/mysql-test/main/disabled.def @@ -16,4 +16,3 @@ mysql_embedded : Bug#12561297 2011-05-14 Anitha Dependent on PB2 chang file_contents : MDEV-6526 these files are not installed anymore max_statement_time : cannot possibly work, depends on timing partition_open_files_limit : open_files_limit check broken by MDEV-18360 -gis-json : MDEV-39813 infinite recursion diff --git a/mysql-test/main/gis-json.result b/mysql-test/main/gis-json.result index 49ec773945866..7546ac6b31cc8 100644 --- a/mysql-test/main/gis-json.result +++ b/mysql-test/main/gis-json.result @@ -132,10 +132,7 @@ repeat('{"type":"GeometryCollection","geometries":[', 2000), '{"type":"Point","coordinates":[0,0]}', repeat(']}', 2000) )) as exp; -exp -NULL -Warnings: -Warning 4040 Limit of 32 on JSON nested structures depth is reached in argument 1 to function 'st_geomfromgeojson' at position 473 +ERROR HY000: Thread stack overrun: # bytes used of a # byte stack, and # bytes needed. Consider increasing the thread_stack system variable. # End of 10.6 tests # # MDEV-34079: ST_AsGeoJSON returns incorrect value for empty geometry diff --git a/mysql-test/main/gis-json.test b/mysql-test/main/gis-json.test index ca402d1c2ef43..b2ecfa2e988aa 100644 --- a/mysql-test/main/gis-json.test +++ b/mysql-test/main/gis-json.test @@ -63,6 +63,9 @@ SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"type": ["POINT"], "coINates": [0,0] }')) --echo # MDEV-39813 ST_GeomFromGeoJSON does not control recursion depth --echo # +--replace_regex /[0-9]+ bytes used of a [0-9]+ byte stack, and [0-9]+ bytes needed/# bytes used of a # byte stack, and # bytes needed/ + +--error ER_STACK_OVERRUN_NEED_MORE SELECT ST_GeomFromGeoJSON( concat( repeat('{"type":"GeometryCollection","geometries":[', 2000), diff --git a/sql/spatial.cc b/sql/spatial.cc index 73304b815188e..269a858da0efe 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -20,6 +20,7 @@ #include "spatial.h" #include "gstream.h" // Gis_read_stream #include "sql_string.h" // String +#include "sql_parse.h" // check_stack_overrun #include /* This is from item_func.h. Didn't want to #include the whole file. */ @@ -586,8 +587,10 @@ Geometry *Geometry::create_from_json(Geometry_buffer *buffer, uint key_len; int fcoll_type_found= 0, feature_type_found= 0; + if (check_stack_overrun(current_thd, STACK_MIN_SIZE , NULL)) + return NULL; + const uint32_t *killed_ptr= (uint32_t *) je->killed_ptr; - int stack_p; if (json_read_value(je)) goto err_return; @@ -753,10 +756,8 @@ Geometry *Geometry::create_from_json(Geometry_buffer *buffer, create_geom: - stack_p= je->stack_p; json_scan_start(je, je->s.cs, coord_start, je->s.str_end); je->killed_ptr= killed_ptr; - je->stack_p= stack_p; if (res->reserve(1 + 4, 512)) goto err_return; @@ -770,10 +771,8 @@ Geometry *Geometry::create_from_json(Geometry_buffer *buffer, return result; handle_geometry_key: - stack_p= je->stack_p; json_scan_start(je, je->s.cs, geometry_start, je->s.str_end); je->killed_ptr= killed_ptr; - je->stack_p= stack_p; return create_from_json(buffer, je, er_on_3D, res); err_return: