Skip to content

Range-check integral double literals in IntegerJsonHandler - #1450

Open
bjornblissing wants to merge 2 commits into
CesiumGS:mainfrom
bjornblissing:fix/integer-json-handler-range-check
Open

bjornblissing wants to merge 2 commits into
CesiumGS:mainfrom
bjornblissing:fix/integer-json-handler-range-check

Conversation

@bjornblissing

@bjornblissing bjornblissing commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Description

IntegerJsonHandler<T>::readDouble (CesiumJsonReader/include/CesiumJsonReader/IntegerJsonHandler.h) already rejects JSON numbers with a fractional part, but an integral-valued double (e.g. 1e20) was cast directly to T with static_cast<T>(intPart) and no range check. If the value's magnitude is outside the range of T, that static_cast is undefined behavior and can silently produce a garbage value.

This matters beyond generic robustness: fields such as bufferView.byteOffset and bufferView.byteLength are parsed through this handler, so a crafted JSON document with an out-of-range float-like literal for one of these fields can inject a garbage value that later flows into buffer bounds checks.

The fix rejects out-of-range integral doubles the same way non-integral ones are already rejected, by forwarding to JsonHandler::readDouble so a warning is reported instead of performing the unchecked cast. The range comparison avoids static_cast<double>(std::numeric_limits<T>::max()), since for 64-bit types that rounds up to 2^63 (or 2^64), which would incorrectly admit the first out-of-range value; instead it compares against the exclusive upper bound 2^digits, which is always exactly representable as a double.

Issue number or link

N/A

Author checklist

  • I have submitted a Contributor License Agreement (only needed once).
  • I have done a full self-review of my code.
  • I have updated CHANGES.md with a short summary of my change (for user-facing changes).
  • I have added or updated unit tests to ensure consistent code coverage as necessary.
  • I have updated the documentation as necessary.

Testing plan

  1. Parse a JSON document with an integer-typed field (e.g. bufferView.byteOffset) set to a float-like literal whose magnitude is outside the range of the target type, such as 1e20 for an int64_t field. Before the fix, this reaches an undefined-behavior static_cast and may produce a garbage value silently. After the fix, readDouble rejects it and a warning is reported, matching the existing behavior for non-integral doubles.
  2. Verify literals at and near the type's boundaries (e.g. values equal to, just below, and just above std::numeric_limits<T>::max()/lowest()) are classified correctly, including for 64-bit integer types where the naive static_cast<double>(max()) comparison would be off by using a rounded bound.
  3. Verify ordinary integral JSON numbers within range (including negative values and zero) continue to parse successfully with no new warnings.

This is a targeted validation fix in the JSON integer parsing path with no user-facing format or API changes.

IntegerJsonHandler<T>::readDouble rejects non-integral doubles, but
cast integral ones directly to T with no range check. A JSON number
written in float-like lexical form with a magnitude outside the range
of T (e.g. 1e20 for an int64_t field) therefore reached a static_cast
that is undefined behavior for out-of-range conversions and can
silently produce a garbage value. Fields typed this way include
bufferView.byteOffset/byteLength, so a garbage value can propagate
into buffer bounds checks elsewhere.

Reject out-of-range values the same way non-integral doubles are
already rejected, by forwarding to JsonHandler::readDouble so a
warning is reported instead of performing the unchecked cast.

The bounds cannot be compared via static_cast<double>(max()): for
64-bit types that rounds up to 2^63 (or 2^64), which would admit the
first out-of-range value. Compare against the exclusive upper bound
2^digits instead, which is always exactly representable as a double.
Cover IntegerJsonHandler<T>::readDouble's boundary behavior for
int64_t and uint64_t directly, and extend the glTF accessor count
test with out-of-range integral doubles (1e20, 2^63). These values
previously produced garbage results via undefined-behavior casts
instead of being rejected.
@j9liu j9liu added this to the November 2026 Release milestone Sep 28, 2026
@j9liu
j9liu requested a review from timoore September 28, 2026 17:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants