Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Out-of-Memory Program Abort in wabt::interp::Table::Grow() #2396

Closed
mobsceneZ opened this issue Mar 4, 2024 · 2 comments
Closed

Out-of-Memory Program Abort in wabt::interp::Table::Grow() #2396

mobsceneZ opened this issue Mar 4, 2024 · 2 comments

Comments

@mobsceneZ
Copy link

Environment

OS               : Linux 5.15.133.1-microsoft-standard-WSL2 #1 SMP Thu Oct 5 21:02:42 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
Commit           : 9fdd024249b6b181d98a4164700ca6ee09f970d9 - 1471dffee8bf9939044b80d34256956a28138e96
Version          : 1.0.33 (git~1.0.33-35-gdddc03d3)
Clang Verison    : 13.0.0
Build            : mkdir build && cd build && export CC=clang CXX=clang++ CFLAGS="-fsanitize=address -g" CXXFLAGS="-fsanitize=address -g" && cmake .. && cmake --build .
Affected Tool    : wasm-interp
Enabled Features : None
Impact           : OOM Program Abort (leading to DoS)

Proof of Concept

wasm-poc-01.zip

Stack Trace Provide By AddressSanitizer

=================================================================
==2643==ERROR: AddressSanitizer: allocator is out of memory trying to allocate 0x57ee38a60 bytes
    #0 0x519508 in operator new(unsigned long) /home/lain/llvm-project-llvmorg-13.0.0/compiler-rt/lib/asan/asan_new_delete.cpp:95
    #1 0x55815b in __gnu_cxx::new_allocator<wabt::interp::Ref>::allocate(unsigned long, void const*) /usr/lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/ext/new_allocator.h:114:27
    #2 0x558100 in std::allocator_traits<std::allocator<wabt::interp::Ref> >::allocate(std::allocator<wabt::interp::Ref>&, unsigned long) /usr/lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/alloc_traits.h:443:20
    #3 0x557cef in std::_Vector_base<wabt::interp::Ref, std::allocator<wabt::interp::Ref> >::_M_allocate(unsigned long) /usr/lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/stl_vector.h:343:20
    #4 0x6a9113 in std::vector<wabt::interp::Ref, std::allocator<wabt::interp::Ref> >::_M_default_append(unsigned long) /usr/lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/vector.tcc:635:34
    #5 0x632e55 in std::vector<wabt::interp::Ref, std::allocator<wabt::interp::Ref> >::resize(unsigned long) /usr/lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/stl_vector.h:937:4
    #6 0x603dca in wabt::interp::Table::Grow(wabt::interp::Store&, unsigned int, wabt::interp::Ref) /home/lain/wabt_asan/src/interp/interp.cc:510:15
    #7 0x626bd2 in wabt::interp::Thread::DoTableGrow(wabt::interp::Instr, wabt::interp::RefPtr<wabt::interp::Trap>*) /home/lain/wabt_asan/src/interp/interp.cc:2162:21
    #8 0x6185c5 in wabt::interp::Thread::StepInternal(wabt::interp::RefPtr<wabt::interp::Trap>*) /home/lain/wabt_asan/src/interp/interp.cc:1520:31
    #9 0x60ffbb in wabt::interp::Thread::Run(int, wabt::interp::RefPtr<wabt::interp::Trap>*) /home/lain/wabt_asan/src/interp/interp.cc:1086:19
    #10 0x602500 in wabt::interp::Thread::Run(wabt::interp::RefPtr<wabt::interp::Trap>*) /home/lain/wabt_asan/src/interp/interp.cc:1078:14
    #11 0x60173f in wabt::interp::DefinedFunc::DoCall(wabt::interp::Thread&, std::vector<wabt::interp::Value, std::allocator<wabt::interp::Value> > const&, std::vector<wabt::interp::Value, std::allocator<wabt::interp::Value> >&, wabt::interp::RefPtr<wabt::interp::Trap>*) /home/lain/wabt_asan/src/interp/interp.cc:428:19
    #12 0x600c0d in wabt::interp::Func::Call(wabt::interp::Store&, std::vector<wabt::interp::Value, std::allocator<wabt::interp::Value> > const&, std::vector<wabt::interp::Value, std::allocator<wabt::interp::Value> >&, wabt::interp::RefPtr<wabt::interp::Trap>*, wabt::Stream*) /home/lain/wabt_asan/src/interp/interp.cc:394:10
    #13 0x60dd76 in wabt::interp::Instance::Instantiate(wabt::interp::Store&, wabt::interp::Ref, std::vector<wabt::interp::Ref, std::allocator<wabt::interp::Ref> > const&, wabt::interp::RefPtr<wabt::interp::Trap>*) /home/lain/wabt_asan/src/interp/interp.cc:944:22
    #14 0x526175 in InstantiateModule(std::vector<wabt::interp::Ref, std::allocator<wabt::interp::Ref> >&, wabt::interp::RefPtr<wabt::interp::Module> const&, wabt::interp::RefPtr<wabt::interp::Instance>*) /home/lain/wabt_asan/src/tools/wasm-interp.cc:340:19
    #15 0x51fc12 in ReadAndRunModule(char const*) /home/lain/wabt_asan/src/tools/wasm-interp.cc:423:3
    #16 0x51ecf7 in ProgramMain(int, char**) /home/lain/wabt_asan/src/tools/wasm-interp.cc:450:25
    #17 0x51ff21 in main /home/lain/wabt_asan/src/tools/wasm-interp.cc:456:10
    #18 0x7fc987d97082 in __libc_start_main /build/glibc-SzIz7B/glibc-2.31/csu/../csu/libc-start.c:308:16

==2643==HINT: if you don't care about these errors you may set allocator_may_return_null=1
SUMMARY: AddressSanitizer: out-of-memory /home/lain/llvm-project-llvmorg-13.0.0/compiler-rt/lib/asan/asan_new_delete.cpp:95 in operator new(unsigned long)
==2643==ABORTING
@adamrk
Copy link

adamrk commented Mar 8, 2024

I tried running this myself and didn't hit the OOM, but I do see the following right before execution traps on an out-of-bounds table access:

000232 func[7]:
 000233: 02 7c                      | local[3..4] type=f64
 000235: 05 7d                      | local[5..9] type=f32
 000237: 01 7b                      | local[10] type=v128
 000239: 02 7f                      | local[11..12] type=i32
 00023b: 01 70                      | local[13] type=funcref
 00023d: 02 40                      | block
 00023f: 02 40                      |   block
 000241: 42 96 cb 93 d2 97 a2 d5 87 |     i64.const -3094160885426100842
 00024a: 55                         | 
 00024b: 10 03                      |     call 3 <i64.no_fold_f64_u>
 00024d: d0 6f                      |     ref.null extern
 00024f: 41 bc e2 f1 fe 7a          |     i32.const 2950459708
 000255: fc 0f 01                   |     table.grow 1             <------------- Grow table by ~3 billion entries
 000258: 11 06 00                   |     call_indirect 6 0        <------------- Trap with oob table access

Adding the entries from that table.grow instruction will allocate about

2950459708 * 8 = 23603677664 = 0x57EE389E0 bytes

which is pretty close to the amount you see in the failed allocation. So I assume your setup just isn't allowing WSL to allocate that much?

Anyway, there's nothing in the spec that prevents a table from growing this large, so it seems perfectly reasonable for wasm-interp to handle the request. I'd think a user that's concerned about possible DOS attacks would check Wasms to ensure tables and memories have a reasonable maximum size (or modify them to add maximum sizes) before executing.

@mobsceneZ
Copy link
Author

I tried running this myself and didn't hit the OOM, but I do see the following right before execution traps on an out-of-bounds table access:

000232 func[7]:
 000233: 02 7c                      | local[3..4] type=f64
 000235: 05 7d                      | local[5..9] type=f32
 000237: 01 7b                      | local[10] type=v128
 000239: 02 7f                      | local[11..12] type=i32
 00023b: 01 70                      | local[13] type=funcref
 00023d: 02 40                      | block
 00023f: 02 40                      |   block
 000241: 42 96 cb 93 d2 97 a2 d5 87 |     i64.const -3094160885426100842
 00024a: 55                         | 
 00024b: 10 03                      |     call 3 <i64.no_fold_f64_u>
 00024d: d0 6f                      |     ref.null extern
 00024f: 41 bc e2 f1 fe 7a          |     i32.const 2950459708
 000255: fc 0f 01                   |     table.grow 1             <------------- Grow table by ~3 billion entries
 000258: 11 06 00                   |     call_indirect 6 0        <------------- Trap with oob table access

Adding the entries from that table.grow instruction will allocate about

2950459708 * 8 = 23603677664 = 0x57EE389E0 bytes

which is pretty close to the amount you see in the failed allocation. So I assume your setup just isn't allowing WSL to allocate that much?

Anyway, there's nothing in the spec that prevents a table from growing this large, so it seems perfectly reasonable for wasm-interp to handle the request. I'd think a user that's concerned about possible DOS attacks would check Wasms to ensure tables and memories have a reasonable maximum size (or modify them to add maximum sizes) before executing.

Thanks for your reply @adamrk, that's a fair point. I shall not consider this kind of issue as bug 🤔 .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants