Skip to content

Bounds-check XtcBasedEncoder and Lossless_bypass against corrupted input#135

Open
alexey-milovidov wants to merge 1 commit into
szcompressor:masterfrom
ClickHouse:ch-bounds-check-xtc
Open

Bounds-check XtcBasedEncoder and Lossless_bypass against corrupted input#135
alexey-milovidov wants to merge 1 commit into
szcompressor:masterfrom
ClickHouse:ch-bounds-check-xtc

Conversation

@alexey-milovidov

Copy link
Copy Markdown

Problem

XtcBasedEncoder::decode and Lossless_bypass::decompress read sizes and indices from the compressed data without validating them. On corrupted or adversarial input (e.g. when fuzzing the decompressor) this leads to out-of-bounds writes/reads and a memory leak:

  • XtcBasedEncoder::decode allocates buffer.data twice — the first allocation is immediately overwritten and leaked.
  • smallIdx, read from the compressed data, indexes the fixed-size magicInts table (magicInts[smallIdx], magicInts[smallIdx - 1]) with no range check — an out-of-bounds read of a static array.
  • buffer.index, an attacker-controlled byte count read from the data, is memcpy'd into buffer.data without checking it against the buffer capacity — a heap buffer overflow.
  • The buffer.data / index-buffer allocations are not null-checked.
  • Lossless_bypass::decompress does not null-check its malloc before memcpy.

Changes

Bounds checks and null checks only; valid data is unaffected:

  • drop the leaked first buffer.data allocation;
  • reject smallIdx outside [0, LASTIDX) before indexing magicInts;
  • reject a packed size (buffer.index) larger than the buffer.data capacity before the copy loop;
  • null-check the buffer.data and index-buffer allocations;
  • null-check the Lossless_bypass allocation.

Notes

XtcBasedEncoder::decode does not receive a length for its input buffer, so the reads from the input pointer itself can not be fully bounded without threading a length through the EncoderInterface::decode signature; this change bounds the out-of-bounds writes and the static-array index, which are the memory-corruption issues.

Context

Found while integrating SZ3 as an experimental compression codec in ClickHouse and fuzzing the decompressor: ClickHouse/ClickHouse#108788

XtcBasedEncoder::decode and Lossless_bypass::decompress read sizes and indices
from the compressed data without validation, so corrupted input could write or
read out of bounds and leak memory:
 - XtcBasedEncoder::decode allocated buffer.data twice (the first allocation was
   overwritten and leaked); the index smallIdx, read from the data, indexed the
   fixed-size magicInts table out of bounds; and buffer.index, an attacker-
   controlled byte count, was memcpy'd into buffer.data without checking it
   against the buffer capacity (a heap buffer overflow). Allocations were not
   null-checked.
 - Lossless_bypass::decompress did not null-check its malloc.

Adds the missing bounds and null checks; valid data is unaffected.

Found while integrating SZ3 into ClickHouse and fuzzing the decompressor:
ClickHouse/ClickHouse#108788
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