schemagen builder crashes on scalar root values when root schema is object/array #16

Closed
opened 2026-06-21 11:25:54 +00:00 by weaselbot · 0 comments
Member

The generated RootBuilder crashes (undefined behavior / assertion failure) when the JSON document root is a scalar value or null while the schema declares a non-nullable object or array root.

Relevant generated-code paths in contrib/schemagen/weaseljson_schemagen.py:

  • Lines 788-793 (_ctor_body): only pushes an initial Kind::RootScalar frame when the root is a scalar. For object/array roots the stack starts empty.
  • Lines 795-824 (_begin_container): handles empty-stack container starts by pushing a root frame, but only for { and [ events.
  • Lines 964-1000 (cbStringData), 1002-1042 (cbNumberData), 1044-1057 (cbBool), and 1061-1072 (cbNull): all call Frame &f = stack_.back(); without first checking stack_.empty().

Reproduction using the existing example.schema.json (root object):

RootBuilder b;
std::string json = "null";   // also "true", "123", or "\"hi\""
WeaselJsonStatus s = b.feed(json.data(), (int)json.size());
if (s == WeaselJson_AGAIN) s = b.finish();

Actual result: assertion failure at std::vector::back() because stack_ is empty:

/usr/include/c++/.../stl_vector.h:...: __builtin_expect(!this->_M_impl._M_start, true): Assertion '!this->empty()' failed.

Expected result: WeaselJson_REJECT (the JSON value does not match the object/array root type).

The same crash occurs for a root array schema when the input is any scalar or null.

Impact: any schema with an object or array root will crash instead of rejecting malformed/untrusted input that happens to be a scalar or null. This contradicts the parser's documented claim of being "robust to crashes with untrusted input" and makes the generated builder unsuitable for directly handling untrusted documents.

The generated `RootBuilder` crashes (undefined behavior / assertion failure) when the JSON document root is a scalar value or `null` while the schema declares a non-nullable object or array root. **Relevant generated-code paths in `contrib/schemagen/weaseljson_schemagen.py`:** - Lines 788-793 (`_ctor_body`): only pushes an initial `Kind::RootScalar` frame when the root is a scalar. For object/array roots the stack starts empty. - Lines 795-824 (`_begin_container`): handles empty-stack container starts by pushing a root frame, but only for `{` and `[` events. - Lines 964-1000 (`cbStringData`), 1002-1042 (`cbNumberData`), 1044-1057 (`cbBool`), and 1061-1072 (`cbNull`): all call `Frame &f = stack_.back();` without first checking `stack_.empty()`. **Reproduction** using the existing `example.schema.json` (root object): ```cpp RootBuilder b; std::string json = "null"; // also "true", "123", or "\"hi\"" WeaselJsonStatus s = b.feed(json.data(), (int)json.size()); if (s == WeaselJson_AGAIN) s = b.finish(); ``` Actual result: assertion failure at `std::vector::back()` because `stack_` is empty: ``` /usr/include/c++/.../stl_vector.h:...: __builtin_expect(!this->_M_impl._M_start, true): Assertion '!this->empty()' failed. ``` Expected result: `WeaselJson_REJECT` (the JSON value does not match the object/array root type). The same crash occurs for a root array schema when the input is any scalar or `null`. **Impact:** any schema with an object or array root will crash instead of rejecting malformed/untrusted input that happens to be a scalar or `null`. This contradicts the parser's documented claim of being "robust to crashes with untrusted input" and makes the generated builder unsuitable for directly handling untrusted documents.
weaselbot was assigned by andrew 2026-06-24 17:24:38 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: weaselab/weaseljson#16