schemagen integer parsing accepts out-of-range negatives and rejects valid positive boundary values #19

Closed
opened 2026-06-21 13:22:29 +00:00 by weaselbot · 0 comments
Member

The generated cbNumberData fallback for integer slots uses std::from_chars on a double to validate numbers written in decimal or exponent form. Because double has only 53 bits of precision, values near the int64 boundary round incorrectly, so the builder both accepts out-of-range negative integers and rejects valid positive integers.

Relevant generator code in contrib/schemagen/weaseljson_schemagen.py:

  • Lines 1002-1040 (cbNumberData template).
  • Specifically lines 1015-1032, the fallback path that parses as double and compares against ±9223372036854775808.0.

Reproduction using a schema whose root (or any field) is "type": "integer":

JSON input 9223372036854775807.0 (valid int64):

  • std::from_chars<int64_t> parses 9223372036854775807 and stops at ..
  • Fallback parses as double: d == 9223372036854775808.0 (rounded up).
  • d >= 9223372036854775808.0 is true, so the builder rejects.
  • Actual result: WeaselJson_REJECT.
  • Expected result: WeaselJson_OK.

JSON input -9223372036854775809 (out of int64 range):

  • std::from_chars<int64_t> returns result_out_of_range.
  • Fallback parses as double: d == -9223372036854775808.0 (rounded to min).
  • d < -9223372036854775808.0 is false, so the builder accepts.
  • Actual result: WeaselJson_OK, and the stored value is -9223372036854775808.
  • Expected result: WeaselJson_REJECT.

The same pattern affects any integer field; e.g. {"name":"x","age":9223372036854775807.0} is rejected and {"name":"x","age":-9223372036854775809} is accepted and stored as the minimum int64.

Impact: documents that are schema-valid can be rejected, and documents that exceed the target type can be silently truncated, which is a numeric overflow bug for untrusted input.

The generated `cbNumberData` fallback for `integer` slots uses `std::from_chars` on a `double` to validate numbers written in decimal or exponent form. Because double has only 53 bits of precision, values near the int64 boundary round incorrectly, so the builder both accepts out-of-range negative integers and rejects valid positive integers. Relevant generator code in `contrib/schemagen/weaseljson_schemagen.py`: - Lines 1002-1040 (`cbNumberData` template). - Specifically lines 1015-1032, the fallback path that parses as `double` and compares against `±9223372036854775808.0`. Reproduction using a schema whose root (or any field) is `"type": "integer"`: JSON input `9223372036854775807.0` (valid int64): - `std::from_chars<int64_t>` parses `9223372036854775807` and stops at `.`. - Fallback parses as double: `d == 9223372036854775808.0` (rounded up). - `d >= 9223372036854775808.0` is true, so the builder rejects. - Actual result: `WeaselJson_REJECT`. - Expected result: `WeaselJson_OK`. JSON input `-9223372036854775809` (out of int64 range): - `std::from_chars<int64_t>` returns `result_out_of_range`. - Fallback parses as double: `d == -9223372036854775808.0` (rounded to min). - `d < -9223372036854775808.0` is false, so the builder accepts. - Actual result: `WeaselJson_OK`, and the stored value is `-9223372036854775808`. - Expected result: `WeaselJson_REJECT`. The same pattern affects any integer field; e.g. `{"name":"x","age":9223372036854775807.0}` is rejected and `{"name":"x","age":-9223372036854775809}` is accepted and stored as the minimum int64. Impact: documents that are schema-valid can be rejected, and documents that exceed the target type can be silently truncated, which is a numeric overflow bug for untrusted input.
weaselbot was assigned by andrew 2026-06-23 22:01:08 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: weaselab/weaseljson#19