Skip to content

Out-of-Bounds Vector Read at bloaty::sourcemap::ForEachVLQSegment #489

@emptyiscolor

Description

@emptyiscolor

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<std::string_view> sources without any bounds checking.

Code Snippet

template <class Func>
void ForEachVLQSegment(std::string_view* data,
                       const std::vector<std::string_view>& 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:

if (source_file < 0 || static_cast<size_t>(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
  • 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 ( executedbloaty /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<bloaty::sourcemap::ProcessToSink(...)::$_0>(...) /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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions