Skip to content

Fix memory safety bugs in block reading and decompression#1320

Open
sharadboni wants to merge 1 commit intogoogle:mainfrom
sharadboni:fix-block-read-memory-safety
Open

Fix memory safety bugs in block reading and decompression#1320
sharadboni wants to merge 1 commit intogoogle:mainfrom
sharadboni:fix-block-read-memory-safety

Conversation

@sharadboni
Copy link
Copy Markdown

Summary

Fix three security-relevant bugs that can be triggered by maliciously crafted SST files:

  • ReadBlock integer overflow (table/format.cc): handle.size() is a uint64_t read from the SST file. On 32-bit platforms the static_cast<size_t> silently truncates, and on all platforms n + kBlockTrailerSize can wrap to a small value, causing an undersized heap allocation followed by an out-of-bounds write. Added an overflow check before the allocation.

  • ZSTD_getFrameContentSize sentinel mishandling (port/port_stdcxx.h): ZSTD_getFrameContentSize returns ZSTD_CONTENTSIZE_UNKNOWN (SIZE_MAX) or ZSTD_CONTENTSIZE_ERROR (SIZE_MAX-1) on failure, but only the value 0 was rejected. Passing SIZE_MAX as the output buffer size to ZSTD_decompressDCtx leads to a massive allocation or undefined behavior. Now both sentinels are properly checked.

  • DecodeEntry uint32 overflow (table/block.cc): The bounds check (limit - p) < (non_shared + value_length) performs the addition in uint32_t, which can wrap to a small value and bypass the check, leading to out-of-bounds reads. Split into two sequential checks that cannot overflow.

Test plan

  • Verify existing unit tests still pass (make check / cmake --build . --target leveldb_tests)
  • Confirm the ReadBlock overflow check rejects a BlockHandle with size() near SIZE_MAX
  • Confirm Zstd_GetUncompressedLength returns false for frames that yield ZSTD_CONTENTSIZE_UNKNOWN or ZSTD_CONTENTSIZE_ERROR
  • Confirm DecodeEntry returns nullptr when non_shared + value_length would overflow uint32

Fix three security-relevant bugs that can be triggered by crafted SST files:

1. ReadBlock integer overflow (table/format.cc): handle.size() is a uint64_t
   read from the SST file. On 32-bit platforms the static_cast<size_t>
   silently truncates, and on all platforms `n + kBlockTrailerSize` can wrap
   to a small value, causing an undersized heap allocation followed by an
   out-of-bounds write. Add an overflow check before the allocation.

2. ZSTD_getFrameContentSize sentinel mishandling (port/port_stdcxx.h): The
   function returns ZSTD_CONTENTSIZE_UNKNOWN (SIZE_MAX) or
   ZSTD_CONTENTSIZE_ERROR (SIZE_MAX-1) on failure, but only the value 0 was
   rejected. Passing SIZE_MAX as the output buffer size to ZSTD_decompressDCtx
   leads to a massive allocation or undefined behavior. Check both sentinels.

3. DecodeEntry uint32 overflow (table/block.cc): The bounds check
   `(limit - p) < (non_shared + value_length)` performs the addition in
   uint32_t, which can wrap to a small value and bypass the check, leading
   to out-of-bounds reads. Split into two sequential checks that cannot
   overflow.
@sharadboni
Copy link
Copy Markdown
Author

@pwnall Could you review this security fix? It addresses an integer overflow in ReadBlock, Zstd sentinel mishandling, and a uint32 overflow in DecodeEntry that can be triggered via crafted SST files.

@ma7moud48
Copy link
Copy Markdown

ma7moud48 commented Apr 15, 2026 via email

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