Skip to content

EncodedS2LaxPolygonShape: heap-buffer-overflow in edge() via CELL_IDS point vector #678

Description

@sushant-me

Heap-buffer-overflow in EncodedS2LaxPolygonShape::edge() on malformed input

A malformed encoded index triggers an ASan heap-buffer-overflow (READ of size 24)
in EncodedS2LaxPolygonShape::edge().

Stack

ERROR: AddressSanitizer: heap-buffer-overflow on address ... (READ of size 24)
  #0 __asan_memcpy
  #1 EncodedS2LaxPolygonShape::edge(int) const
  #2 main /tmp/s2_repro_full.cc:24   (shape->edge(edge_id))

Root cause

EncodedS2LaxPolygonShape::Init() decodes loop_starts_ (an
EncodedUintVector<uint32_t>) but never validates its contents. edge() →
chain_edge(i, j) then computes

int start = loop_starts_[i];
return Edge(vertices_[start + j], vertices_[start + k]);

so a loop_starts_[i] value beyond the real vertex count makes
EncodedS2PointVector::operator[] (CELL_IDS format) index past the decoded block
data, and DecodeCellIdsFormat() performs the unchecked memcpy of the 24-byte
S2Point.

(Note: this is a refinement of my initial analysis. I first suspected the CELL_IDS
block-count arithmetic in InitCellIdsFormat(), but that path is already guarded by
the int32_t-max check. The reproducible trigger is the unvalidated loop_starts_,
which is what the fix addresses.)

The non-encoded S2LaxPolygonShape::Init() has the same missing validation; PR #675
fixes both variants.

Reproduction

323-byte input (base64, decode to a file and pass to the traversal reproducer):

EDmHBQEDAJUAEAABDI5CxsXB7z+Jcwt+Gjq2PwKBl7YwB2iR7z/GPwkRY+OvOt8/KDOol30I7D//8r7xc9qTID+QPcF9W5DQPyjgCBAIBQt+GjrGPwkRY+OvIiIpIiIiIiJzk8F9kNA/KOAIEHFycXCPbGwAAAAyM3wyMjIybGxsbGxsbG1sbHhtAHh4eHh4eHh4eHh4eHh4eHh4eDh4eHgCbGxswrjWRbY/l7YwB2iR7z/GPwkRY+OvOt8/KDOol30I7D//8jq2PwKBwrjsP//yvvFz2pMgP5A9wX1bkNA/KOAIEAgFC34aOsY/CRFj468iIiIiIiIiInOTwX2Q0D8o4AgQeHh4eHh4eHh4eHh4eHg4eHh4bGxsbAcAAAAAAAAAco+Pj2xsAAAAMjJ8MjIyMmxsbGxsbGxsbGxsbGxsAAE=

Init() returns true; the documented traversal then triggers the overflow in edge().

Impact

Out-of-bounds read (memory corruption / information disclosure) via __asan_memcpy
of an S2Point (24 bytes).

Related to #674/#676/#677 (all in the encoded-index decode path), but a distinct
class: an actual heap-buffer-overflow rather than a null-deref or OOM.

Fixed by PR #675.

Activity

  1. added a commit that references this issue on Sep 15, 2026
    63aec31
  2. sushant-me commented on Sep 15, 2026

    @sushant-me
    Author

    Fixed in PR #675 (commit Validate loop_starts in EncodedS2LaxPolygonShape::Init). The encoded variant was missing the same loop_starts validation as the non-encoded class. Verified: the reproducer now completes cleanly, and all four crash inputs (#674/#676/#677/#678) no longer crash under ASan.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions