WebAssembly / WebAssembly/wabt

NULL Pointer Dereference in wabt::interp::(anonymous namespace)::BinaryReaderInterp::BeginFunctionBody

Open
#2,565 1 comment 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

Affected Projects

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

Problem Type

CWE-476: NULL Pointer Dereference

Description

Summary

A null pointer write vulnerability was discovered in the wabt::interp::(anonymous namespace)::BinaryReaderInterp::BeginFunctionBody function within glslang. This issue occurs when processing certain files, leading to a potential application crash.

Details

The vulnerability arises in the wabt::interp::(anonymous namespace)::BinaryReaderInterp::BeginFunctionBody function defined in src/interp/binary-reader-interp.cc at line 829.
The reason is that the function BinaryReaderInterp::BeginFunctionBody did not check the validity of func_ before calling func_->code_offset, resulting in a null pointer dereference write.

Result BinaryReaderInterp::BeginFunctionBody(Index index, Offset size) {
  Index defined_index = index - num_func_imports();
  func_ = &module_.funcs[defined_index];
  func_->code_offset = istream_.end();    //null pointer write

  depth_fixups_.Clear();
  label_stack_.clear();

  // The fixups map is keyed by actual function index (not defined_index)
  // (function imports don't have code and won't appear in the fixups map,
  // but they still use function indexes)
  func_fixups_.Resolve(istream_, index);

  CHECK_RESULT(validator_.BeginFunctionBody(GetLocation(), index));

  // Push implicit func label (equivalent to return).
  // With exception handling it acts as a catch-less try block, which is
  // needed to support delegating to the caller of a function using the
  // try-delegate instruction.
  PushLabel(LabelKind::Try, Istream::kInvalidOffset, Istream::kInvalidOffset,
            func_->handlers.size());
  return Result::Ok;
}

Image

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:

BeginFunctionBody-npd.zip

./read_binary_interp_fuzzer ./BeginFunctionBody-npd

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

ASAN Report
Running: ./BeginFunctionBody-npd
AddressSanitizer:DEADLYSIGNAL
=================================================================
==7012==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000058 (pc 0x58d4645edfec bp 0x7ffdec0c7090 sp 0x7ffdec0c6f00 T0)
==7012==The signal is caused by a WRITE memory access.
==7012==Hint: address points to the zero page.
    #0 0x58d4645edfec in wabt::interp::(anonymous namespace)::BinaryReaderInterp::BeginFunctionBody(unsigned int, unsigned long) /fuzz/project/wabt/src/interp/binary-reader-interp.cc:829:22
    #1 0x58d464778cd3 in wabt::(anonymous namespace)::BinaryReader::ReadCodeSection(unsigned long) /fuzz/project/wabt/src/binary-reader.cc:2870:5
    #2 0x58d464766cdd in wabt::(anonymous namespace)::BinaryReader::ReadSections(wabt::(anonymous namespace)::BinaryReader::ReadSectionsOptions const&) /fuzz/project/wabt/src/binary-reader.cc:3045:26
    #3 0x58d464763b99 in wabt::(anonymous namespace)::BinaryReader::ReadModule(wabt::(anonymous namespace)::BinaryReader::ReadModuleOptions const&) /fuzz/project/wabt/src/binary-reader.cc:3119:3
    #4 0x58d464762803 in wabt::ReadBinary(void const*, unsigned long, wabt::BinaryReaderDelegate*, wabt::ReadBinaryOptions const&) /fuzz/project/wabt/src/binary-reader.cc:3141:17
    #5 0x58d4645e6a7c 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:1742:10
    #6 0x58d4645bf23c in LLVMFuzzerTestOneInput /fuzz/project/read_binary_interp_fuzzer.cc:39:3
    #7 0x58d4644e4ce3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzz/fuzzers/read_binary_interp_fuzzer+0xf7ce3) (BuildId: c0207dc8f08f1b5960d5be29ebfea0e2d13262a0)
    #8 0x58d4644cea5f in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/fuzz/fuzzers/read_binary_interp_fuzzer+0xe1a5f) (BuildId: c0207dc8f08f1b5960d5be29ebfea0e2d13262a0)
    #9 0x58d4644d47b6 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/fuzz/fuzzers/read_binary_interp_fuzzer+0xe77b6) (BuildId: c0207dc8f08f1b5960d5be29ebfea0e2d13262a0)
    #10 0x58d4644fe5d2 in main (/fuzz/fuzzers/read_binary_interp_fuzzer+0x1115d2) (BuildId: c0207dc8f08f1b5960d5be29ebfea0e2d13262a0)
    #11 0x7cf2bd71bd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #12 0x7cf2bd71be3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #13 0x58d4644c9324 in _start (/fuzz/fuzzers/read_binary_interp_fuzzer+0xdc324) (BuildId: c0207dc8f08f1b5960d5be29ebfea0e2d13262a0)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /fuzz/project/wabt/src/interp/binary-reader-interp.cc:829:22 in wabt::interp::(anonymous namespace)::BinaryReaderInterp::BeginFunctionBody(unsigned int, unsigned long)
==7012==ABORTING

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 in src/interp/binary-reader-interp.cc at BinaryReaderInterp::BeginFunctionBody and trace the call from BinaryReader::ReadCodeSection. Build with AddressSanitizer and run the provided read_binary_interp_fuzzer harness with BeginFunctionBody-npd; done means the malformed input no longer causes an invalid write or crash.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
security
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
50/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.