Skip to content

validate shape ids when decoding S2ShapeIndexCell - #698

Open
sayed0200 wants to merge 1 commit into
google:masterfrom
sayed0200:shape-index-cell-shape-id-bounds
Open

sayed0200 wants to merge 1 commit into
google:masterfrom
sayed0200:shape-index-cell-shape-id-bounds

Conversation

@sayed0200

Copy link
Copy Markdown

S2ShapeIndexCell::Decode guards decoded shape ids against INT32_MAX but not against num_shape_ids, so an encoded index whose cell references a shape id past the end of the index decodes without error. The index.shape(clipped.shape_id()) lookup that essentially every query performs while walking the index then indexes shapes_ out of bounds:

==ERROR: AddressSanitizer: heap-buffer-overflow
READ of size 8 ...
  index.shape(5000) -> shapes_[5000]   (shapes_.size() == 2)

Reproduced by hand-encoding a one-cell index whose clipped shape carries shape id 5000, decoding it with a 2-shape ShapeFactory, and reading the referenced shape. The added regression test decodes the same blob and expects Init to reject it.

The fix rejects out-of-range shape ids inside the decode loop, next to the existing INT32_MAX guard.

S2ShapeIndexCell::Decode only guarded decoded shape ids against INT32_MAX,
not against num_shape_ids. A crafted encoded index whose cell names a shape
id past the end of the index decodes fine, and the usual
shape(clipped.shape_id()) lookup then reads out of bounds.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces validation to reject out-of-range shape IDs during decoding in S2ShapeIndexCell::Decode, preventing potential out-of-bounds reads, and adds a corresponding unit test. The reviewer recommends validating the shape_id immediately after it is updated in each branch of the conditional block, rather than at the end of the loop, to prevent implicit narrowing conversions of unvalidated values when calling clipped->Init.

Comment thread src/s2/s2shape_index.cc
// Shape ids come from the encoded data, so reject any that are out of
// range. Otherwise a caller that indexes S2ShapeIndex::shape() with a
// decoded shape id reads out of bounds.
if (shape_id >= num_shape_ids) return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

While this check successfully prevents out-of-bounds access during queries, clipped->Init(shape_id, ...) is called earlier in the loop body before this validation occurs. Since shape_id is an int64_t and clipped->Init expects an int32_t, an extremely large shape_id from malformed encoded data could trigger an implicit narrowing conversion of an unvalidated value before the check is reached.\n\nTo improve robustness and adhere to defensive programming practices, consider validating shape_id immediately after it is updated in each of the three branches of the if-else statement (i.e., right before clipped->Init is called), rather than waiting until the end of the loop.

This branch has not been deployed

No deployments
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.

1 participant