Fix out-of-bounds read in SerializationReplicated on the native protocol - #112331
Fix out-of-bounds read in SerializationReplicated on the native protocol#112331Avogar wants to merge 1 commit into
Conversation
`SerializationReplicated::deserializeBinaryBulkWithMultipleStreams` read a
client-supplied replicated-index buffer from the native TCP protocol and
validated only the index row count, not the index values. Every
`ColumnReplicated` accessor then dereferences `nested_column[index]` without
a bound, so a crafted `REPLICATED`-serialized column whose indexes point past
the nested ("elements") column produces an out-of-bounds read when the column
is materialized.
Validate every deserialized index against the number of nested elements
(throwing `INCORRECT_DATA`) before attaching the indexes, mirroring the
per-discriminator check already done in `SerializationVariant`.
Reachable only over the native TCP protocol (negotiated revision >=
`DBMS_MIN_REVISION_WITH_REPLICATED_SERIALIZATION`); the format layer builds
`NativeReader` with `server_revision = 0`, so `REPLICATED` is never read there.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
Workflow [PR], commit [aca7466] AI ReviewSummaryThis PR hardens Findings❌ Blockers
Tests
Final VerdictChanges requested. |
| checkDeserializedIndexes(*indexes, size_of_indexes_type, num_elements); | ||
| column_replicated.getIndexes().attachIndexes(std::move(indexes)); | ||
|
|
||
| nested->deserializeBinaryBulkWithMultipleStreams(column_replicated.getNestedColumn(), 0, num_elements, settings, state, cache); |
There was a problem hiding this comment.
checkDeserializedIndexes only proves the replicated indexes are < num_elements; we still never prove that nested->deserializeBinaryBulkWithMultipleStreams actually produced num_elements rows. That matters because primitive bulk readers such as SerializationNumber::deserializeBinaryBulk short-read on EOF instead of throwing, and NativeReader::readData only checks the top-level column->size(). For ColumnReplicated that size is the index count, not the nested column size, so a client can send in-range indexes, advertise num_elements = N, truncate the elements payload, and still hand an invalid ColumnReplicated to executor.push, preserving the out-of-bounds read when the block is materialized.
Please mirror the post-read consistency check that SerializationArray and SerializationVariant do here: after the nested deserialization, verify column_replicated.getNestedColumn()->size() == num_elements and throw INCORRECT_DATA on mismatch. A regression with valid indexes plus a truncated elements stream would cover the remaining case.
Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):
Fix out-of-bounds read in SerializationReplicated on the native protocol on corrupted indexes data.