Skip to content

[Bug] Heap-use-after-free in wabt::AST::InsertNode within wasm-decompile #2676

@oneafter

Description

@oneafter

Description

We encountered a Heap-use-after-free (UAF) vulnerability in the wasm-decompile tool of the WABT project. The crash occurs during the decompilation process when wabt::AST::InsertNode attempts to read from a memory region that has already been freed.

Unlike common assertion failures, this issue was reproduced in a Release build (with NDEBUG defined) using AddressSanitizer, indicating a critical memory corruption vulnerability that affects production environments.

Environment

  • OS: Linux x86_64
  • Complier: Clang
  • Build Configuration: Release mode with ASan enabled.
  • Build Command:
make clang-release-asan CMAKE_FLAGS="-DCMAKE_BUILD_TYPE=Release -DUSE_ASAN=ON -DCMAKE_CXX_FLAGS_RELEASE=-DNDEBUG"

Vulnerability Details

  • Target: wasm-decompile
  • Crash Type: Heap-use-after-free (CWE-416)
  • Faulting Instruction: READ of size 8
  • Location: wabt::AST::InsertNode (wasm-decompile+0x1c0949)
  • Root Cause Analysis: The ASan report indicates a use-after-free scenario:
  1. Allocation: Memory was allocated by operator new via std::_Rb_tree..._M_insert_range_unique (likely inserting into a map/set structure).
  2. Free: The memory was subsequently freed by operator delete in ProgramMain -> std::_Rb_tree..._M_erase.
  3. Use: The freed memory was later accessed by wabt::AST::InsertNode during wabt::Decompiler::Decompile.

The stack trace suggests that the AST construction logic is referencing data that has been prematurely destroyed in the main program flow.

Reproduce

./wasm-decompile ./repro

Download Link: repro

ASAN report

==65817==ERROR: AddressSanitizer: heap-use-after-free on address 0x504000000a20 at pc 0x56263508594a bp 0x7ffc2d924fe0 sp 0x7ffc2d924fd8
READ of size 8 at 0x504000000a20 thread T0
    #0 0x562635085949 in wabt::AST::InsertNode(wabt::NodeType, wabt::ExprType, wabt::Expr const*, unsigned int) (/src/repro/wabt/bin/wasm-decompile+0x1c0949) (BuildId: b44cadef6a2094e740cb6ce71ee9a45cfac22974)
    #1 0x56263507c83a in wabt::AST::Construct(wabt::intrusive_list<wabt::Expr> const&, unsigned int, unsigned int, bool) (/src/repro/wabt/bin/wasm-decompile+0x1b783a) (BuildId: b44cadef6a2094e740cb6ce71ee9a45cfac22974)
    #2 0x5626350774f1 in wabt::Decompiler::Decompile[abi:cxx11]() (/src/repro/wabt/bin/wasm-decompile+0x1b24f1) (BuildId: b44cadef6a2094e740cb6ce71ee9a45cfac22974)
    #3 0x5626350752a6 in wabt::Decompile[abi:cxx11](wabt::Module const&, wabt::DecompileOptions const&) (/src/repro/wabt/bin/wasm-decompile+0x1b02a6) (BuildId: b44cadef6a2094e740cb6ce71ee9a45cfac22974)
    #4 0x562635011941 in ProgramMain(int, char**) (/src/repro/wabt/bin/wasm-decompile+0x14c941) (BuildId: b44cadef6a2094e740cb6ce71ee9a45cfac22974)
    #5 0x7ff08f0ef1c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #6 0x7ff08f0ef28a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #7 0x562634f35584 in _start (/src/repro/wabt/bin/wasm-decompile+0x70584) (BuildId: b44cadef6a2094e740cb6ce71ee9a45cfac22974)

0x504000000a20 is located 16 bytes inside of 48-byte region [0x504000000a10,0x504000000a40)
freed by thread T0 here:
    #0 0x56263500f271 in operator delete(void*) (/src/repro/wabt/bin/wasm-decompile+0x14a271) (BuildId: b44cadef6a2094e740cb6ce71ee9a45cfac22974)
    #1 0x5626350794ef in std::_Rb_tree<std::basic_string_view<char, std::char_traits<char>>, std::basic_string_view<char, std::char_traits<char>>, std::_Identity<std::basic_string_view<char, std::char_traits<char>>>, std::less<std::basic_string_view<char, std::char_traits<char>>>, std::allocator<std::basic_string_view<char, std::char_traits<char>>>>::_M_erase(std::_Rb_tree_node<std::basic_string_view<char, std::char_traits<char>>>*) (/src/repro/wabt/bin/wasm-decompile+0x1b44ef) (BuildId: b44cadef6a2094e740cb6ce71ee9a45cfac22974)
    #2 0x562635011919 in ProgramMain(int, char**) (/src/repro/wabt/bin/wasm-decompile+0x14c919) (BuildId: b44cadef6a2094e740cb6ce71ee9a45cfac22974)
    #3 0x7ff08f0ef1c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #4 0x7ff08f0ef28a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #5 0x562634f35584 in _start (/src/repro/wabt/bin/wasm-decompile+0x70584) (BuildId: b44cadef6a2094e740cb6ce71ee9a45cfac22974)

previously allocated by thread T0 here:
    #0 0x56263500e9f1 in operator new(unsigned long) (/src/repro/wabt/bin/wasm-decompile+0x1499f1) (BuildId: b44cadef6a2094e740cb6ce71ee9a45cfac22974)
    #1 0x56263509d8a0 in std::enable_if<__same_value_type<std::basic_string_view<char, std::char_traits<char>> const*>::value, void>::type std::_Rb_tree<std::basic_string_view<char, std::char_traits<char>>, std::basic_string_view<char, std::char_traits<char>>, std::_Identity<std::basic_string_view<char, std::char_traits<char>>>, std::less<std::basic_string_view<char, std::char_traits<char>>>, std::allocator<std::basic_string_view<char, std::char_traits<char>>>>::_M_insert_range_unique<std::basic_string_view<char, std::char_traits<char>> const*>(std::basic_string_view<char, std::char_traits<char>> const*, std::basic_string_view<char, std::char_traits<char>> const*) (/src/repro/wabt/bin/wasm-decompile+0x1d88a0) (BuildId: b44cadef6a2094e740cb6ce71ee9a45cfac22974)

SUMMARY: AddressSanitizer: heap-use-after-free (/src/repro/wabt/bin/wasm-decompile+0x1c0949) (BuildId: b44cadef6a2094e740cb6ce71ee9a45cfac22974) in wabt::AST::InsertNode(wabt::NodeType, wabt::ExprType, wabt::Expr const*, unsigned int)
Shadow bytes around the buggy address:
  0x504000000780: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fd
  0x504000000800: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fd
  0x504000000880: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fd
  0x504000000900: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fd
  0x504000000980: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fd
=>0x504000000a00: fa fa fd fd[fd]fd fd fd fa fa 00 00 00 00 00 00
  0x504000000a80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x504000000b00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x504000000b80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x504000000c00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x504000000c80: 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
==65817==ABORTING

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions