Skip to content

Bounds-check per-entry key read in compact_tuple_sketch::deserialize - #511

Merged
proost merged 2 commits into
apache:masterfrom
SavicStefan:tuple_fix
Aug 10, 2026
Merged

Bounds-check per-entry key read in compact_tuple_sketch::deserialize#511
proost merged 2 commits into
apache:masterfrom
SavicStefan:tuple_fix

Conversation

@SavicStefan

Copy link
Copy Markdown
Contributor

The entry-reading loop reads each entry as an 8-byte key followed by a Summary-width summary. The summary read is passed the remaining capacity and bounds-checks itself, but the key read is an unchecked fixed-size copy_from_mem that relies solely on the pre-loop keys-only reservation, which assumes every entry's summary occupies the width Summary serializes to.

When that assumption does not hold (a truncated buffer, or a buffer whose serialized summary width differs from the Summary it is deserialized as) the read cursor advances past the data. num_entries is read from the preamble and is unaffected, so the loop runs the full count and a later key read walks off the end of the buffer: silent on a normal build, a heap-buffer-overflow under AddressSanitizer.

Add an ensure_minimum_memory check before the key read, mirroring the remaining-capacity check the summary read already performs and the up-front size validation the compact theta parser does. A malformed buffer now throws std::out_of_range instead of reading out of bounds.

Adds a tuple_sketch_test case that deserializes a float-summary sketch as a double-summary sketch and asserts std::out_of_range.

The entry-reading loop reads each entry as an 8-byte key followed by a
Summary-width summary. The summary read is passed the remaining capacity and
bounds-checks itself, but the key read is an unchecked fixed-size copy_from_mem
that relies solely on the pre-loop keys-only reservation, which assumes every
entry's summary occupies the width Summary serializes to.

When that assumption does not hold (a truncated buffer, or a buffer whose
serialized summary width differs from the Summary it is deserialized as) the
read cursor advances past the data. num_entries is read from the preamble and
is unaffected, so the loop runs the full count and a later key read walks off
the end of the buffer: silent on a normal build, a heap-buffer-overflow under
AddressSanitizer.

Add an ensure_minimum_memory check before the key read, mirroring the
remaining-capacity check the summary read already performs and the up-front
size validation the compact theta parser does. A malformed buffer now throws
std::out_of_range instead of reading out of bounds.

Adds a tuple_sketch_test case that deserializes a float-summary sketch as a
double-summary sketch and asserts std::out_of_range.

Co-authored-by: SavicStefan <50296686+SavicStefan@users.noreply.github.com>

@proost proost left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The implementation change looks appropriate to me. But I suggest revising the test case to focus on truncated/malformed-buffer safety.

Comment thread tuple/test/tuple_sketch_test.cpp Outdated
@SavicStefan
SavicStefan requested a review from proost August 10, 2026 14:58

@proost proost left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you!

@proost
proost merged commit a001ebe into apache:master Aug 10, 2026
17 checks passed
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