WebAssembly / WebAssembly/wabt

[BUG] A container-overflow in `wabt::interp::(anonymous namespace)::BinaryReaderInterp::BeginFunctionBody(unsigned int, unsigned long)` at `src/interp/binary-reader-interp.cc:829:22`

Open
#2,585 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
8.1k
Forks
827
Avg merge
4d 6h
Merged PRs (30d)
18

Description

Description

  • Version: Commit a60eb26
  • Environment:Ubuntu 20.04.6 LTS, Clang 18.1.8

Steps to reproduce

export CC="clang"
export CXX="clang++"
export CFLAGS="-fsanitize=address -g -O0 -fno-omit-frame-pointer"
export CXXFLAGS="-fsanitize=address -g -O0 -fno-omit-frame-pointer -stdlib=libc++"
export LIB_FUZZING_ENGINE="-fsanitize=fuzzer"

mkdir build
cd build
cmake ..  -DBUILD_TESTS=OFF
cmake --build . --parallel
cd ..

wget https://github.com/google/oss-fuzz/raw/refs/heads/master/projects/wabt/read_binary_interp_fuzzer.cc
$CXX $CXXFLAGS -std=c++17 -I. -Ibuild -Iinclude -Ibuild/include \
  ./src/read_binary_interp_fuzzer.cc $LIB_FUZZING_ENGINE ./build/libwabt.a \
  -o ./read_binary_interp_fuzzer
wget https://github.com/user-attachments/files/19825078/wabt_crash_2.txt
./read_binary_interp_fuzzer wasm_crash_2.txt

Sanitizer output

==15==ERROR: AddressSanitizer: container-overflow on address 0x611000000098 at pc 0x00000047263b bp 0x7fff2d851c70 sp 0x7fff2d851c68
WRITE of size 4 at 0x611000000098 thread T0
    #0 0x47263a in wabt::interp::(anonymous namespace)::BinaryReaderInterp::BeginFunctionBody(unsigned int, unsigned long) /src/wabt/src/interp/binary-reader-interp.cc:829:22
    #1 0x8db52c in wabt::(anonymous namespace)::BinaryReader::ReadCodeSection(unsigned long) /src/wabt/src/binary-reader.cc:2870:5
    #2 0x8db52c in wabt::(anonymous namespace)::BinaryReader::ReadSections(wabt::(anonymous namespace)::BinaryReader::ReadSectionsOptions const&) /src/wabt/src/binary-reader.cc:3045:26
    #3 0x8bb810 in wabt::(anonymous namespace)::BinaryReader::ReadModule(wabt::(anonymous namespace)::BinaryReader::ReadModuleOptions const&) /src/wabt/src/binary-reader.cc:3119:3
    #4 0x8bb810 in wabt::ReadBinary(void const*, unsigned long, wabt::BinaryReaderDelegate*, wabt::ReadBinaryOptions const&) /src/wabt/src/binary-reader.cc:3141:17
    #5 0x523f97 in wabt::interp::ReadBinaryInterp(std::__1::basic_string_view<char, std::__1::char_traits<char> >, void const*, unsigned long, wabt::ReadBinaryOptions const&, std::__1::vector<wabt::Error, std::__1::allocator<wabt::Error> >*, wabt::interp::ModuleDesc*) /src/wabt/src/interp/binary-reader-interp.cc:1742:10
    #6 0x40f28f in LLVMFuzzerTestOneInput /src/read_binary_interp_fuzzer.cc:39:3
    #7 0xadd54a in main (/out/read_binary_interp_fuzzer.fuzz+0xadd54a)
    #8 0x77b398ec7082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082)
    #9 0x40980d in _start (/out/read_binary_interp_fuzzer.fuzz+0x40980d)

0x611000000098 is located 88 bytes inside of 240-byte region [0x611000000040,0x611000000130)
allocated by thread T0 here:
    #0 0xb4de7d in malloc (/out/read_binary_interp_fuzzer.fuzz+0xb4de7d)
    #1 0x77b3995ffb28 in operator new(unsigned long) (/lib/x86_64-linux-gnu/libstdc++.so.6+0xaab28)
    #2 0x45af94 in wabt::interp::(anonymous namespace)::BinaryReaderInterp::OnFunctionCount(unsigned int) /src/wabt/src/interp/binary-reader-interp.cc:585:17
    #3 0x8ce61f in wabt::(anonymous namespace)::BinaryReader::ReadFunctionSection(unsigned long) /src/wabt/src/binary-reader.cc:2696:3
    #4 0x8ce61f in wabt::(anonymous namespace)::BinaryReader::ReadSections(wabt::(anonymous namespace)::BinaryReader::ReadSectionsOptions const&) /src/wabt/src/binary-reader.cc:3017:26
    #5 0x8bb810 in wabt::(anonymous namespace)::BinaryReader::ReadModule(wabt::(anonymous namespace)::BinaryReader::ReadModuleOptions const&) /src/wabt/src/binary-reader.cc:3119:3
    #6 0x8bb810 in wabt::ReadBinary(void const*, unsigned long, wabt::BinaryReaderDelegate*, wabt::ReadBinaryOptions const&) /src/wabt/src/binary-reader.cc:3141:17
    #7 0x523f97 in wabt::interp::ReadBinaryInterp(std::__1::basic_string_view<char, std::__1::char_traits<char> >, void const*, unsigned long, wabt::ReadBinaryOptions const&, std::__1::vector<wabt::Error, std::__1::allocator<wabt::Error> >*, wabt::interp::ModuleDesc*) /src/wabt/src/interp/binary-reader-interp.cc:1742:10
    #8 0x40f28f in LLVMFuzzerTestOneInput /src/read_binary_interp_fuzzer.cc:39:3
    #9 0xadd54a in main (/out/read_binary_interp_fuzzer.fuzz+0xadd54a)

HINT: if you don't care about these errors you may set ASAN_OPTIONS=detect_container_overflow=0.
If you suspect a false positive see also: https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow.
SUMMARY: AddressSanitizer: container-overflow /src/wabt/src/interp/binary-reader-interp.cc:829:22 in wabt::interp::(anonymous namespace)::BinaryReaderInterp::BeginFunctionBody(unsigned int, unsigned long)
Shadow bytes around the buggy address:
  0x0c227fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c227fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c227fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c227fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c227fff8000: fa fa fa fa fa fa fa fa fc fc fc fc fc fc fc fc
=>0x0c227fff8010: fc fc fc[fc]fc fc fc fc fc fc fc fc fc fc fc fc
  0x0c227fff8020: fc fc fc fc fc fc fa fa fa fa fa fa fa fa fa fa
  0x0c227fff8030: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c227fff8040: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c227fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==15==ABORTING

POC

wabt_crash_2.txt

Credit

Reported by Yifan Zhang, PLL

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/interp/binary-reader-interp.cc at BeginFunctionBody(unsigned int, unsigned long), especially line 829, and compare it with allocation in OnFunctionCount at line 585. Reproduce using the provided ASan build, read_binary_interp_fuzzer.cc, and wabt_crash_2.txt. Done means the POC no longer produces the reported container-overflow.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, wasm
Domain
compilers, security
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.