WebAssembly / WebAssembly/wabt
NULL Pointer Dereference in wabt::interp::(anonymous namespace)::BinaryReaderInterp::BeginFunctionBody
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;
}
PoC
Steps to reproduce:
- 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 ..
- 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
- Run the fuzzer to trigger the segmentation fault:
./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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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