HexBrain

MariaDB · Report #3896507

Server crash (stack-overflow DoS) via unbounded recursion in MariaDB WKB GEOMETRYCOLLECTION parser

Validstatus
mediumseverity
18upvotes
11 Oct 2026disclosed

How it was found

Reported via email as per policy to [email protected] on July 4 but received no response., hence reporting here.

Server crash (stack-overflow DoS) via unbounded recursion in MariaDB WKB GEOMETRYCOLLECTION parser

Target: MariaDB Server 13.1.0-alpha, source rev bd366d81b80aa65fc76ec570fa0bf218a996853b (fresh, 2026-07-01) Component: spatial / GIS WKB parser — sql/spatial.cc Class: CWE-674 Uncontrolled Recursion → stack exhaustion → SIGSEGV (Denial of Service) Status: Confirmed end-to-end against a live ASan server (SIGSEGV reproduced; depth-differential proven).


Summary

Gis_geometry_collection::init_from_wkb() and ::get_data_size() recurse once per nested child geometry when parsing a WKB (Well-Known Binary) GEOMETRYCOLLECTION, with no recursion-depth limit and no check_stack_overrun() call. A single crafted value with a few thousand levels of nested GEOMETRYCOLLECTION exhausts the thread stack and crashes the entire server (all connections dropped). Reachable by any user who can call a spatial function or write a GEOMETRY value — i.e. a low-privileged authenticated attacker.

Notably, MariaDB guards recursion everywhere else (check_stack_overrun is used in item.cc, item_func.cc, item_cmpfunc.cc, item_subselect.cc, item_jsonfunc.cc, json_table.cc, json_schema.cc, …). The geometry WKB parser is the outlier — it has no such guard (grep -rn check_stack_overrun sql/spatial.cc sql/spatial.h sql/item_geofunc.cc → no matches). This is a cross-path asymmetry: the protection pattern exists project-wide but was never applied to this parser.


Root cause

sql/spatial.cc:

uint Gis_geometry_collection::init_from_wkb(const char *wkb, uint len, ...) {   // :4414
  ...
  while (n_geom--) {                                                             // :4429 (attacker n_geom)
    Geometry_buffer buffer;                                                      // :4431 stack frame per level
    Geometry *geom = create_by_typeid(&buffer, wkb_type);
    if (!(g_len = geom->init_from_wkb(wkb, len, bo, res)))                       // :4447 RECURSE
      return 0;
    ...
  }
}
uint32 Gis_geometry_collection::get_data_size() const {                          // :4284
  ...
  while (n_objects--) {
    if ((object_size = geom->get_data_size()) == GET_SIZE_ERROR) ...             // :4308 RECURSE
  }
}

Each nesting level consumes one C++ stack frame (holding a Geometry_buffer). With the default thread_stack (~292 KB), a few thousand levels overflow the stack → SIGSEGV inside the recursion. There is no check_stack_overrun() and no depth cap on either path.

Reachable entry points (whole geometry front-end family — check_stack_overrun appears ZERO times in sql/spatial.cc and sql/item_geofunc.cc):

  • WKB — Item_func_geometry_from_wkb::val_str → Geometry::create_from_wkb → Gis_geometry_collection::init_from_wkb (spatial.cc:4414, recurse :4447). ST_GeomFromWKB, ST_GeomCollFromWKB.
  • WKT (text) — Geometry::create_from_wkt (spatial.cc:393) → Gis_geometry_collection::init_from_wkt → recurses via create_from_wkt (spatial.cc:4342). ST_GeomFromText, ST_GeomCollFromText.
  • GeoJSON — Gis_geometry_collection::init_from_json recurses via child init_from_json (spatial.cc:767). ST_GeomFromGeoJSON.
  • Stored value — Field_geom::store → is_binary_valid() → get_data_size() (recursive form) — INSERT/UPDATE of a crafted value into a GEOMETRY column.

All four share the single missing check_stack_overrun in the geometry-collection recursion.


Evidence (live ASan server, MariaDB 13.1.0-alpha, -DWITH_ASAN=ON)

Malicious WKB = little-endian nested GEOMETRYCOLLECTION (type 7): 0107000000 + 010000000107000000 × K + 00000000 (each 9-byte unit adds one nesting level).

Depth differential on one server instance (--thread-stack=299008):

TestK (nesting)Result
Sanity: ST_GeomFromWKB(<point>)0returns normally
Shallow: SELECT ST_GeomFromWKB(0x…)50rc=0, server alive
Deep: SELECT ST_GeomFromWKB(0x…)120000client rc=1, server DIED, exit/sig=139 (SIGSEGV)

Reproduced across two independent runs. A normal/shallow geometry parses fine; only the deep nesting crashes — conclusively a depth-dependent recursion (stack overflow), not a malformed- byte parse bug.

WKT front-end (sibling entry point) — independently confirmed on the same server: SELECT ST_GeomFromText('GEOMETRYCOLLECTION(' ×K ... 'POINT(1 1)' ')' ×K): K=50 returns normally (server alive); K=100000 → server DIED, exit signal 139 (SIGSEGV). Same depth-dependent stack overflow via the WKT create_from_wkt recursion (spatial.cc:4342). GeoJSON (ST_GeomFromGeoJSON → init_from_json, spatial.cc:767) recurses identically with no guard (code-confirmed). Reproducer: reports/mariadb_poc/wkt_poc.sh. (ASan emits no textual report here because the stack is exhausted before its handler can run — the process takes a raw SIGSEGV, which is the stack-overflow signature.)

Reproducer scripts: reports/mariadb_poc/wkb_poc.sh, wkb_diff.sh (generate the WKB and drive the server). Minimal manual repro:

-- build 0x0107000000 + 010000000107000000*120000 + 00000000 and:
SELECT ST_GeomFromWKB(0x0107000000...00000000);   -- server crashes (SIGSEGV)

Impact

AspectAssessment
EffectFull mariadbd crash (SIGSEGV) → all client connections dropped; repeatable = sustained DoS
PrivilegeLow — any account able to run a spatial function or write a GEOMETRY column
VectorAV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H → CVSS 3.1 ≈ 6.5 (Medium)
RCENo — stack exhaustion crash, not a controllable memory write

Suggested fix

Call check_stack_overrun() at the top of Gis_geometry_collection::init_from_wkb and ::get_data_size (as done throughout the rest of the server), or enforce a maximum GEOMETRYCOLLECTION nesting depth, returning a clean parse error instead of recursing.

Impact

AspectAssessment
EffectFull mariadbd crash (SIGSEGV) → all client connections dropped; repeatable = sustained DoS
PrivilegeLow — any account able to run a spatial function or write a GEOMETRY column
VectorAV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H → CVSS 3.1 ≈ 6.5 (Medium)
RCENo — stack exhaustion crash, not a controllable memory write

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