Skip to content

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

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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions