WebAssembly/binaryen

[Segmentation fault] Out-of-bounds read in wasm::FunctionValidator::visitBlock

Closed this issue · 0 comments

Hi,

I identified an out-of-bounds data read bug when fuzzing the wasm-opt tool. This bug is triggered when attempting to validate a malformed WAST file containing a BLOCK instruction with no defined functions.

Steps to reproduce:

Test file: wast-oob-read.zip
Command: ./wasm-opt ./wast-oob-read.wast

GDB:

gdb --args ./wasm-opt ./wast-oob-read.wast
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04.2) 12.1

Reading symbols from ./wasm-opt...
(gdb) run
Starting program: /home/sofi/fuzzing/binaryen/executables/Debug/binaryen/bin/wasm-opt ./wast-oob-read.wast
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff7600640 (LWP 904392)]
[New Thread 0x7ffff6c00640 (LWP 904393)]
[New Thread 0x7ffff6200640 (LWP 904394)]
[New Thread 0x7ffff5800640 (LWP 904395)]
[New Thread 0x7ffff4e00640 (LWP 904396)]
[New Thread 0x7ffff4400640 (LWP 904397)]
[New Thread 0x7ffff3a00640 (LWP 904398)]
[New Thread 0x7ffff3000640 (LWP 904399)]
[New Thread 0x7ffff2600640 (LWP 904400)]
[New Thread 0x7ffff1c00640 (LWP 904401)]
[New Thread 0x7ffff1200640 (LWP 904402)]
[New Thread 0x7ffff0800640 (LWP 904403)]
[New Thread 0x7fffefe00640 (LWP 904404)]
[New Thread 0x7fffef400640 (LWP 904405)]
[New Thread 0x7fffeea00640 (LWP 904406)]
[New Thread 0x7fffee000640 (LWP 904407)]
[New Thread 0x7fffed600640 (LWP 904408)]
[New Thread 0x7fffecc00640 (LWP 904409)]
[New Thread 0x7fffec200640 (LWP 904410)]
[New Thread 0x7fffeb800640 (LWP 904411)]
[New Thread 0x7fffeae00640 (LWP 904412)]
[New Thread 0x7fffea400640 (LWP 904413)]
[New Thread 0x7fffe9a00640 (LWP 904414)]
[New Thread 0x7fffe9000640 (LWP 904415)]

Thread 1 "wasm-opt" received signal SIGSEGV, Segmentation fault.
wasm::FunctionValidator::visitBlock (this=0x7fffffffb9a0, curr=0x555556f8ba60) at /home/sofi/fuzzing/binaryen/executables/Debug/binaryen/src/wasm/wasm-validator.cpp:699
699	  switch (getFunction()->profile) {
(gdb) bt
#0  wasm::FunctionValidator::visitBlock (this=0x7fffffffb9a0, curr=0x555556f8ba60) at /home/sofi/fuzzing/binaryen/executables/Debug/binaryen/src/wasm/wasm-validator.cpp:699
#1  0x00005555561b7a9b in wasm::Walker<wasm::FunctionValidator, wasm::Visitor<wasm::FunctionValidator, void> >::doVisitBlock (self=0x7fffffffb9a0, currp=0x7fffffffb920)
    at /home/sofi/fuzzing/binaryen/executables/Debug/binaryen/src/wasm-delegations.def:18
#2  0x00005555561adcc4 in wasm::Walker<wasm::FunctionValidator, wasm::Visitor<wasm::FunctionValidator, void> >::walk (this=0x7fffffffb9f8, root=@0x7fffffffb920: 0x555556f8ba60)
    at /home/sofi/fuzzing/binaryen/executables/Debug/binaryen/src/wasm-traversal.h:307
#3  0x00005555561ac979 in wasm::FunctionValidator::validate (this=0x7fffffffb9a0, curr=0x555556f8ba60) at /home/sofi/fuzzing/binaryen/executables/Debug/binaryen/src/wasm/wasm-validator.cpp:261
#4  0x00005555561a0483 in operator() (__closure=0x7fffffffbc40, curr=0x555556f5d2e0) at /home/sofi/fuzzing/binaryen/executables/Debug/binaryen/src/wasm/wasm-validator.cpp:3781
#5  0x00005555561a3c5d in wasm::ModuleUtils::iterDefinedGlobals<wasm::validateGlobals(wasm::Module&, wasm::ValidationInfo&)::<lambda(wasm::Global*)> >(wasm::Module &, struct {...}) (wasm=..., visitor=...)
    at /home/sofi/fuzzing/binaryen/executables/Debug/binaryen/src/ir/module-utils.h:158
#6  0x00005555561a0741 in wasm::validateGlobals (module=..., info=...) at /home/sofi/fuzzing/binaryen/executables/Debug/binaryen/src/wasm/wasm-validator.cpp:3763
#7  0x00005555561a2f0f in wasm::WasmValidator::validate (this=0x7fffffffc300, module=..., flags=2) at /home/sofi/fuzzing/binaryen/executables/Debug/binaryen/src/wasm/wasm-validator.cpp:4159
#8  0x00005555561a32bd in wasm::WasmValidator::validate (this=0x7fffffffc300, module=..., options=...) at /home/sofi/fuzzing/binaryen/executables/Debug/binaryen/src/wasm/wasm-validator.cpp:4192
#9  0x000055555565bdf3 in main (argc=2, argv=0x7fffffffe038) at /home/sofi/fuzzing/binaryen/executables/Debug/binaryen/src/tools/wasm-opt.cpp:298

Valgrind:

valgrind ./wasm-opt ./wast-oob-read.wast
==904527== Memcheck, a memory error detector
==904527== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==904527== Using Valgrind-3.24.0.GIT and LibVEX; rerun with -h for copyright info
==904527== Command: ./wasm-opt ./wast-oob-read.wast
==904527== 
==904527== Invalid read of size 4
==904527==    at 0xD4812F: wasm::FunctionValidator::visitBlock(wasm::Block*) (wasm-validator.cpp:699)
==904527==    by 0xD6BA9A: wasm::Walker<wasm::FunctionValidator, wasm::Visitor<wasm::FunctionValidator, void> >::doVisitBlock(wasm::FunctionValidator*, wasm::Expression**) (wasm-delegations.def:18)
==904527==    by 0xD61CC3: wasm::Walker<wasm::FunctionValidator, wasm::Visitor<wasm::FunctionValidator, void> >::walk(wasm::Expression*&) (wasm-traversal.h:307)
==904527==    by 0xD60978: wasm::FunctionValidator::validate(wasm::Expression*) (wasm-validator.cpp:261)
==904527==    by 0xD54482: wasm::validateGlobals(wasm::Module&, wasm::ValidationInfo&)::{lambda(wasm::Global*)#1}::operator()(wasm::Global*) const (wasm-validator.cpp:3781)
==904527==    by 0xD57C5C: void wasm::ModuleUtils::iterDefinedGlobals<wasm::validateGlobals(wasm::Module&, wasm::ValidationInfo&)::{lambda(wasm::Global*)#1}>(wasm::Module&, wasm::validateGlobals(wasm::Module&, wasm::ValidationInfo&)::{lambda(wasm::Global*)#1}) (module-utils.h:158)
==904527==    by 0xD54740: wasm::validateGlobals(wasm::Module&, wasm::ValidationInfo&) (wasm-validator.cpp:3763)
==904527==    by 0xD56F0E: wasm::WasmValidator::validate(wasm::Module&, unsigned int) (wasm-validator.cpp:4159)
==904527==    by 0xD572BC: wasm::WasmValidator::validate(wasm::Module&, wasm::PassOptions const&) (wasm-validator.cpp:4192)
==904527==    by 0x20FDF2: main (wasm-opt.cpp:298)
==904527==  Address 0x40 is not stack'd, malloc'd or (recently) free'd
==904527== 
==904527== 
==904527== Process terminating with default action of signal 11 (SIGSEGV)
==904527==  Access not within mapped region at address 0x40
==904527==    at 0xD4812F: wasm::FunctionValidator::visitBlock(wasm::Block*) (wasm-validator.cpp:699)
==904527==    by 0xD6BA9A: wasm::Walker<wasm::FunctionValidator, wasm::Visitor<wasm::FunctionValidator, void> >::doVisitBlock(wasm::FunctionValidator*, wasm::Expression**) (wasm-delegations.def:18)
==904527==    by 0xD61CC3: wasm::Walker<wasm::FunctionValidator, wasm::Visitor<wasm::FunctionValidator, void> >::walk(wasm::Expression*&) (wasm-traversal.h:307)
==904527==    by 0xD60978: wasm::FunctionValidator::validate(wasm::Expression*) (wasm-validator.cpp:261)
==904527==    by 0xD54482: wasm::validateGlobals(wasm::Module&, wasm::ValidationInfo&)::{lambda(wasm::Global*)#1}::operator()(wasm::Global*) const (wasm-validator.cpp:3781)
==904527==    by 0xD57C5C: void wasm::ModuleUtils::iterDefinedGlobals<wasm::validateGlobals(wasm::Module&, wasm::ValidationInfo&)::{lambda(wasm::Global*)#1}>(wasm::Module&, wasm::validateGlobals(wasm::Module&, wasm::ValidationInfo&)::{lambda(wasm::Global*)#1}) (module-utils.h:158)
==904527==    by 0xD54740: wasm::validateGlobals(wasm::Module&, wasm::ValidationInfo&) (wasm-validator.cpp:3763)
==904527==    by 0xD56F0E: wasm::WasmValidator::validate(wasm::Module&, unsigned int) (wasm-validator.cpp:4159)
==904527==    by 0xD572BC: wasm::WasmValidator::validate(wasm::Module&, wasm::PassOptions const&) (wasm-validator.cpp:4192)
==904527==    by 0x20FDF2: main (wasm-opt.cpp:298)
==904527==  If you believe this happened as a result of a stack
==904527==  overflow in your program's main thread (unlikely but
==904527==  possible), you can try to increase the size of the
==904527==  main thread stack using the --main-stacksize= flag.
==904527==  The main thread stack size used in this run was 8388608.
==904527== 
==904527== HEAP SUMMARY:
==904527==     in use at exit: 260,524 bytes in 2,043 blocks
==904527==   total heap usage: 3,606 allocs, 1,563 frees, 407,734 bytes allocated
==904527== 
==904527== LEAK SUMMARY:
==904527==    definitely lost: 0 bytes in 0 blocks
==904527==    indirectly lost: 0 bytes in 0 blocks
==904527==      possibly lost: 7,296 bytes in 24 blocks
==904527==    still reachable: 253,228 bytes in 2,019 blocks
==904527==         suppressed: 0 bytes in 0 blocks
==904527== Rerun with --leak-check=full to see details of leaked memory
==904527== 
==904527== For lists of detected and suppressed errors, rerun with: -s
==904527== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault