Unchecked allocation during the parsing of a table cache (.ldb) in a LevelDB database
- Dominant language
- C++
- Stars
- 39.4k
- Forks
- 8.2k
- PR merge metrics
- No merged PRs in 30d
Description
Hi,
I tried to fuzz the code that parses the `.ldb` table cache file. I found an unhandled C++ exception `std::bad__alloc`.
## Description
The `leveldb::Table::Open` function used to parsed the `.ldb` and `.stt` file does not check the size of data blocks before allocating the buffer where the contents of the block will be copied into.
The allocation happens in the `leveldb::ReadBlock` function. The variable `n` is the size of the `BlockHandle` which is a value retrieved from the `.ldb` file.
```cpp
Status ReadBlock(RandomAccessFile* file, const ReadOptions& options,
const BlockHandle& handle, BlockContents* result) {
result->data = Slice();
result->cachable = false;
result->heap_allocated = false;
// Read the block contents as well as the type/crc footer.
// See table_builder.cc for the code that built this structure.
size_t n = static_cast(handle.size());
char* buf = new char[n + kBlockTrailerSize];
```
This function is used by the `leveldbutil` CLI tool when dumping an `.ldb` file but also when database directory contains a table cache file, is opened through a call to `leveldb::DB::Open()`.
## Reports
C++ exception `std::bad_alloc`
```bash
> ~/leveldb/build/leveldbutil dump crash.ldb
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
Aborted
```
With sanitizer:
```bash
> ~/leveldb/fuzzing/table_reader_wkspace/default/crashes$ ~/leveldb/build-afl/leveldbutil dump crash.ldb
=================================================================
==16226==ERROR: AddressSanitizer: requested allocation size 0x41a142833a142855 (0x41a142833a143858 after adjustments for alignment, red zones etc.) exceeds maximum supported size of 0x10000000000 (thread T0)
#0 0x4c565d in operator new[](unsigned long) (/home/pyraun/leveldb/build-afl/leveldbutil+0x4c565d)
#1 0x4e94f3 in leveldb::ReadBlock(leveldb::RandomAccessFile*, leveldb::ReadOptions const&, leveldb::BlockHandle const&, leveldb::BlockContents*) /home/pyraun/leveldb/table/format.cc:73:15
#2 0x4cb1b4 in leveldb::Table::Open(leveldb::Options const&, leveldb::RandomAccessFile*, unsigned long, leveldb::Table**) /home/pyraun/leveldb/table/table.cc:61:7
#3 0x4c9142 in leveldb::(anonymous namespace)::DumpTable(leveldb::Env*, std::__cxx11::basic_string, std::allocator > const&, leveldb::WritableFile*) /home/pyraun/leveldb/db/dumpfile.cc:160:9
#4 0x4c9142 in leveldb::DumpFile(leveldb::Env*, std::__cxx11::basic_string, std::allocator > const&, leveldb::WritableFile*) /home/pyraun/leveldb/db/dumpfile.cc:218:10
#5 0x4c851b in leveldb::(anonymous namespace)::HandleDumpCommand(leveldb::Env*, char**, int) /home/pyraun/leveldb/db/leveldbutil.cc:29:16
#6 0x4c851b in main /home/pyraun/leveldb/db/leveldbutil.cc:57:12
#7 0x7fe0b11f3082 in __libc_start_main /build/glibc-SzIz7B/glibc-2.31/csu/../csu/libc-start.c:308:16
==16226==HINT: if you don't care about these errors you may set allocator_may_return_null=1
SUMMARY: AddressSanitizer: allocation-size-too-big (/home/pyraun/leveldb/build-afl/leveldbutil+0x4c565d) in operator new[](unsigned long)
==16226==ABORTING
```
Contributor guide
Research direction
Read table/format.cc's ReadBlock and trace its callers in table/table.cc; the report also points to db/dumpfile.cc and db/leveldbutil.cc. Run leveldbutil dump on the malformed .ldb reproducer first, then verify parsing rejects the oversized block without terminating with std::bad_alloc.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- databases, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100