Closed Bug 1747562 Opened 2 years ago Closed 2 years ago

AddressSanitizer: heap-use-after-free [@ FunctionCompiler::maybeCreateTryPadBlock] with READ of size 8 with WebAssembly exceptions

Categories

(Core :: JavaScript: WebAssembly, defect, P1)

x86_64
Linux
defect

Tracking

()

RESOLVED FIXED
97 Branch
Tracking Status
firefox-esr91 --- unaffected
firefox95 --- unaffected
firefox96 --- unaffected
firefox97 --- fixed

People

(Reporter: decoder, Assigned: rhunt)

References

(Regression)

Details

(5 keywords, Whiteboard: [bugmon:update,bisect][sec-survey][post-critsmash-triage])

Attachments

(3 files)

The following testcase crashes on mozilla-central revision 20211223-bd494168b95a (fuzzing-asan-opt build, run with --no-threads --wasm-compiler=ion --wasm-exceptions):

let modBuf = new Uint8Array([
  0,97,115,109,1,0,0,0,1,4,1,96,
  0,0,3,2,1,0,7,6,1,2,199,180,0,
  0,10,102,1,100,0,2,64,6,64,16,
  0,16,0,16,0,16,0,16,0,16,0,16,
  0,16,0,16,0,16,0,16,0,16,0,16,
  0,16,0,16,0,16,0,16,0,16,0,16,
  0,16,0,16,0,16,0,16,0,16,0,16,
  0,16,0,24,0,68,180,180,180,180,
  244,180,180,180,158,158,158,177,
  253,12,41,199,199,127,199,199,
  91,199,136,44,98,49,253,127,0,
  0,253,128,1,253,24,4,26,26,11,11
]);
let module = new WebAssembly.Module(modBuf);

Backtrace:

==8540==ERROR: AddressSanitizer: heap-use-after-free on address 0x6110000241c0 at pc 0x55efdf510f90 bp 0x7fffd94d5130 sp 0x7fffd94d5128
READ of size 8 at 0x6110000241c0 thread T0
    #0 0x55efdf510f8f in (anonymous namespace)::FunctionCompiler::maybeCreateTryPadBlock((anonymous namespace)::Control&) js/src/wasm/WasmIonCompile.cpp:2897:32
    #1 0x55efdf50e614 in (anonymous namespace)::FunctionCompiler::finishCatchlessTry((anonymous namespace)::Control&) js/src/wasm/WasmIonCompile.cpp:3124:10
    #2 0x55efdf48730e in EmitEnd js/src/wasm/WasmIonCompile.cpp:3676:14
    #3 0x55efdf48730e in EmitBodyExprs((anonymous namespace)::FunctionCompiler&) js/src/wasm/WasmIonCompile.cpp:5926:14
    #4 0x55efdf47a39f in js::wasm::IonCompileFunctions(js::wasm::ModuleEnvironment const&, js::wasm::CompilerEnvironment const&, js::LifoAlloc&, mozilla::Vector<js::wasm::FuncCompileInput, 8ul, js::SystemAllocPolicy> const&, js::wasm::CompiledCode*, mozilla::UniquePtr<char [], JS::FreePolicy>*) js/src/wasm/WasmIonCompile.cpp:7153:12
    #5 0x55efdf41f9ca in ExecuteCompileTask(js::wasm::CompileTask*, mozilla::UniquePtr<char [], JS::FreePolicy>*) js/src/wasm/WasmGenerator.cpp:726:16
    #6 0x55efdf41fc75 in js::wasm::ModuleGenerator::locallyCompileCurrentTask() js/src/wasm/WasmGenerator.cpp:787:8
    #7 0x55efdf420fdd in js::wasm::ModuleGenerator::finishFuncDefs() js/src/wasm/WasmGenerator.cpp:927:24
    #8 0x55efdf3e75fb in bool DecodeCodeSection<js::wasm::Decoder>(js::wasm::ModuleEnvironment const&, js::wasm::Decoder&, js::wasm::ModuleGenerator&) js/src/wasm/WasmCompile.cpp:680:13
    #9 0x55efdf3e6abc in js::wasm::CompileBuffer(js::wasm::CompileArgs const&, js::wasm::ShareableBytes const&, mozilla::UniquePtr<char [], JS::FreePolicy>*, mozilla::Vector<mozilla::UniquePtr<char [], JS::FreePolicy>, 0ul, js::SystemAllocPolicy>*, JS::OptimizedEncodingListener*) js/src/wasm/WasmCompile.cpp:702:8
    #10 0x55efdf4a6ab1 in js::WasmModuleObject::construct(JSContext*, unsigned int, JS::Value*) js/src/wasm/WasmJS.cpp:1801:7
    #11 0x55efddb3572f in CallJSNative js/src/vm/Interpreter.cpp:425:13
    #12 0x55efddb3572f in CallJSNativeConstructor js/src/vm/Interpreter.cpp:441:8
    #13 0x55efddb3572f in InternalConstruct(JSContext*, js::AnyConstructArgs const&) js/src/vm/Interpreter.cpp:617:14
    #14 0x55efddb1f62f in Interpret(JSContext*, js::RunState&) js/src/vm/Interpreter.cpp:3299:16
    #15 0x55efddb044b1 in js::RunScript(JSContext*, js::RunState&) js/src/vm/Interpreter.cpp:394:13
    #16 0x55efddb36d73 in js::ExecuteKernel(JSContext*, JS::Handle<JSScript*>, JS::Handle<JSObject*>, JS::Handle<JS::Value>, js::AbstractFramePtr, JS::MutableHandle<JS::Value>) js/src/vm/Interpreter.cpp:767:13
    #17 0x55efdddde27b in ExecuteScript(JSContext*, JS::Handle<JSObject*>, JS::Handle<JSScript*>, JS::MutableHandle<JS::Value>) js/src/vm/CompilationAndEvaluation.cpp:515:10
    #18 0x55efdddde585 in JS_ExecuteScript(JSContext*, JS::Handle<JSScript*>) js/src/vm/CompilationAndEvaluation.cpp:539:10
    #19 0x55efdd81f7dd in RunFile(JSContext*, char const*, _IO_FILE*, CompileUtf8, bool) js/src/shell/js.cpp:1065:10
    #20 0x55efdd81e9b9 in Process(JSContext*, char const*, bool, FileKind) js/src/shell/js.cpp:1654:14
    #21 0x55efdd78b679 in ProcessArgs js/src/shell/js.cpp:10991:10
    #22 0x55efdd78b679 in Shell(JSContext*, js::cli::OptionParser*) js/src/shell/js.cpp:11736:12
    #23 0x55efdd77d449 in main js/src/shell/js.cpp:12756:12
    #24 0x7f55550ed82f in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/../csu/libc-start.c:291
    #25 0x55efdd6b7ca3 in _start (/mnt/LangFuzz/work/builds/tc/opt64-fuzzing/dist/bin/js+0xf41ca3)

0x6110000241c0 is located 0 bytes inside of 256-byte region [0x6110000241c0,0x6110000242c0)
freed by thread T0 here:
    #0 0x55efdd734002 in __interceptor_free /builds/worker/fetches/llvm-project/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cpp:111:3
    #1 0x55efdf48723a in ~CompactPairHelper /builds/worker/workspace/obj-build/dist/include/mozilla/CompactPair.h:42:8
    #2 0x55efdf48723a in ~ControlStackEntry js/src/wasm/WasmOpIter.h:233:7
    #3 0x55efdf48723a in popBack /builds/worker/workspace/obj-build/dist/include/mozilla/Vector.h:1477:18
    #4 0x55efdf48723a in popEnd js/src/wasm/WasmOpIter.h:1370:17
    #5 0x55efdf48723a in EmitEnd js/src/wasm/WasmIonCompile.cpp:3665:12
    #6 0x55efdf48723a in EmitBodyExprs((anonymous namespace)::FunctionCompiler&) js/src/wasm/WasmIonCompile.cpp:5926:14
    #7 0x55efdf47a39f in js::wasm::IonCompileFunctions(js::wasm::ModuleEnvironment const&, js::wasm::CompilerEnvironment const&, js::LifoAlloc&, mozilla::Vector<js::wasm::FuncCompileInput, 8ul, js::SystemAllocPolicy> const&, js::wasm::CompiledCode*, mozilla::UniquePtr<char [], JS::FreePolicy>*) js/src/wasm/WasmIonCompile.cpp:7153:12
    #8 0x55efdf41f9ca in ExecuteCompileTask(js::wasm::CompileTask*, mozilla::UniquePtr<char [], JS::FreePolicy>*) js/src/wasm/WasmGenerator.cpp:726:16
    #9 0x55efdf41fc75 in js::wasm::ModuleGenerator::locallyCompileCurrentTask() js/src/wasm/WasmGenerator.cpp:787:8
    #10 0x55efdf420fdd in js::wasm::ModuleGenerator::finishFuncDefs() js/src/wasm/WasmGenerator.cpp:927:24
    #11 0x55efdf3e75fb in bool DecodeCodeSection<js::wasm::Decoder>(js::wasm::ModuleEnvironment const&, js::wasm::Decoder&, js::wasm::ModuleGenerator&) js/src/wasm/WasmCompile.cpp:680:13
    #12 0x55efdf3e6abc in js::wasm::CompileBuffer(js::wasm::CompileArgs const&, js::wasm::ShareableBytes const&, mozilla::UniquePtr<char [], JS::FreePolicy>*, mozilla::Vector<mozilla::UniquePtr<char [], JS::FreePolicy>, 0ul, js::SystemAllocPolicy>*, JS::OptimizedEncodingListener*) js/src/wasm/WasmCompile.cpp:702:8
    #13 0x55efdf4a6ab1 in js::WasmModuleObject::construct(JSContext*, unsigned int, JS::Value*) js/src/wasm/WasmJS.cpp:1801:7
    #14 0x55efddb3572f in CallJSNative js/src/vm/Interpreter.cpp:425:13
    #15 0x55efddb3572f in CallJSNativeConstructor js/src/vm/Interpreter.cpp:441:8
    #16 0x55efddb3572f in InternalConstruct(JSContext*, js::AnyConstructArgs const&) js/src/vm/Interpreter.cpp:617:14
    #17 0x55efddb1f62f in Interpret(JSContext*, js::RunState&) js/src/vm/Interpreter.cpp:3299:16
    #18 0x55efddb044b1 in js::RunScript(JSContext*, js::RunState&) js/src/vm/Interpreter.cpp:394:13
    #19 0x55efddb36d73 in js::ExecuteKernel(JSContext*, JS::Handle<JSScript*>, JS::Handle<JSObject*>, JS::Handle<JS::Value>, js::AbstractFramePtr, JS::MutableHandle<JS::Value>) js/src/vm/Interpreter.cpp:767:13
    #20 0x55efdddde27b in ExecuteScript(JSContext*, JS::Handle<JSObject*>, JS::Handle<JSScript*>, JS::MutableHandle<JS::Value>) js/src/vm/CompilationAndEvaluation.cpp:515:10
    #21 0x55efdddde585 in JS_ExecuteScript(JSContext*, JS::Handle<JSScript*>) js/src/vm/CompilationAndEvaluation.cpp:539:10
    #22 0x55efdd81f7dd in RunFile(JSContext*, char const*, _IO_FILE*, CompileUtf8, bool) js/src/shell/js.cpp:1065:10
    #23 0x55efdd81e9b9 in Process(JSContext*, char const*, bool, FileKind) js/src/shell/js.cpp:1654:14
    #24 0x55efdd78b679 in ProcessArgs js/src/shell/js.cpp:10991:10
    #25 0x55efdd78b679 in Shell(JSContext*, js::cli::OptionParser*) js/src/shell/js.cpp:11736:12
    #26 0x55efdd77d449 in main js/src/shell/js.cpp:12756:12
    #27 0x7f55550ed82f in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/../csu/libc-start.c:291

previously allocated by thread T0 here:
    #0 0x55efdd734563 in __interceptor_realloc /builds/worker/fetches/llvm-project/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cpp:148:3
    #1 0x55efdf513072 in js_arena_realloc /builds/worker/workspace/obj-build/dist/include/js/Utility.h:400:10
    #2 0x55efdf513072 in js_pod_arena_realloc<js::jit::MControlInstruction *> /builds/worker/workspace/obj-build/dist/include/js/Utility.h:605:26
    #3 0x55efdf513072 in maybe_pod_arena_realloc<js::jit::MControlInstruction *> /builds/worker/workspace/obj-build/dist/include/js/AllocPolicy.h:40:12
    #4 0x55efdf513072 in pod_arena_realloc<js::jit::MControlInstruction *> /builds/worker/workspace/obj-build/dist/include/js/AllocPolicy.h:53:12
    #5 0x55efdf513072 in pod_realloc<js::jit::MControlInstruction *> /builds/worker/workspace/obj-build/dist/include/js/AllocPolicy.h:78:12
    #6 0x55efdf513072 in growTo /builds/worker/workspace/obj-build/dist/include/mozilla/Vector.h:209:21
    #7 0x55efdf513072 in mozilla::Vector<js::jit::MControlInstruction*, 8ul, js::SystemAllocPolicy>::growStorageBy(unsigned long) /builds/worker/workspace/obj-build/dist/include/mozilla/Vector.h:1021:10
    #8 0x55efdf512bf2 in growByUninitialized /builds/worker/workspace/obj-build/dist/include/mozilla/Vector.h:1127:9
    #9 0x55efdf512bf2 in bool mozilla::Vector<js::jit::MControlInstruction*, 8ul, js::SystemAllocPolicy>::emplaceBack<js::jit::MControlInstruction*&>(js::jit::MControlInstruction*&) /builds/worker/workspace/obj-build/dist/include/mozilla/Vector.h:669:10
    #10 0x55efdf512a20 in (anonymous namespace)::FunctionCompiler::addPadPatch(js::jit::MControlInstruction*, unsigned long) js/src/wasm/WasmIonCompile.cpp:2790:23
    #11 0x55efdf4e5148 in delegatePadPatches js/src/wasm/WasmIonCompile.cpp:3105:12
    #12 0x55efdf4e5148 in EmitDelegate((anonymous namespace)::FunctionCompiler&) js/src/wasm/WasmIonCompile.cpp:3890:12
    #13 0x55efdf48000b in EmitBodyExprs((anonymous namespace)::FunctionCompiler&) js/src/wasm/WasmIonCompile.cpp:5967:14
    #14 0x55efdf47a39f in js::wasm::IonCompileFunctions(js::wasm::ModuleEnvironment const&, js::wasm::CompilerEnvironment const&, js::LifoAlloc&, mozilla::Vector<js::wasm::FuncCompileInput, 8ul, js::SystemAllocPolicy> const&, js::wasm::CompiledCode*, mozilla::UniquePtr<char [], JS::FreePolicy>*) js/src/wasm/WasmIonCompile.cpp:7153:12
    #15 0x55efdf41f9ca in ExecuteCompileTask(js::wasm::CompileTask*, mozilla::UniquePtr<char [], JS::FreePolicy>*) js/src/wasm/WasmGenerator.cpp:726:16
    #16 0x55efdf41fc75 in js::wasm::ModuleGenerator::locallyCompileCurrentTask() js/src/wasm/WasmGenerator.cpp:787:8
    #17 0x55efdf420fdd in js::wasm::ModuleGenerator::finishFuncDefs() js/src/wasm/WasmGenerator.cpp:927:24
    #18 0x55efdf3e75fb in bool DecodeCodeSection<js::wasm::Decoder>(js::wasm::ModuleEnvironment const&, js::wasm::Decoder&, js::wasm::ModuleGenerator&) js/src/wasm/WasmCompile.cpp:680:13
    #19 0x55efdf3e6abc in js::wasm::CompileBuffer(js::wasm::CompileArgs const&, js::wasm::ShareableBytes const&, mozilla::UniquePtr<char [], JS::FreePolicy>*, mozilla::Vector<mozilla::UniquePtr<char [], JS::FreePolicy>, 0ul, js::SystemAllocPolicy>*, JS::OptimizedEncodingListener*) js/src/wasm/WasmCompile.cpp:702:8
    #20 0x55efdf4a6ab1 in js::WasmModuleObject::construct(JSContext*, unsigned int, JS::Value*) js/src/wasm/WasmJS.cpp:1801:7
    #21 0x55efddb3572f in CallJSNative js/src/vm/Interpreter.cpp:425:13
    [...]
    #33 0x55efdd77d449 in main js/src/shell/js.cpp:12756:12

SUMMARY: AddressSanitizer: heap-use-after-free js/src/wasm/WasmIonCompile.cpp:2897:32 in (anonymous namespace)::FunctionCompiler::maybeCreateTryPadBlock((anonymous namespace)::Control&)
Shadow bytes around the buggy address:
  0x0c227fffc820: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x0c227fffc830: fa fa fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd
  0x0c227fffc840: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
  Heap left redzone:       fa
  Freed heap region:       fd
Attached file Testcase
Group: javascript-core-security

This was found by wasm-smith, marking s-s and sec-high due to use-after-free.

Bugmon Analysis
Unable to reproduce bug 1747562 using build mozilla-central 20211223215735-bd494168b95a. Without a baseline, bugmon is unable to analyze this bug.
Removing bugmon keyword as no further action possible. Please review the bug and re-add the keyword for further analysis.

Keywords: bugmon

Assigning to Ryan for now but certainly this came in with the Ion exception handling patches last week.

Assignee: nobody → rhunt
Status: NEW → ASSIGNED
Priority: -- → P1

This commit reworks the handling of catchless try's to be simpler
and more efficient.

The most significant change is that we now pop the control
item after executing the 'finish' operations in EmitEnd. This is
done to extend the lifetime of the current active Control item
so that it can be used in the 'finish' operations for the
try and body labels. This aligns with how baseline implements
emitEnd, and none of the other labels rely on control item
being popped before executing their finish tasks.

The second change is to handle closing a catchless try as
delegating all of its throwing instructions to the nearest
enclosing try block (if it exists) or else the body delegate
rethrow pad. This simplifies the generated code, as before we
would emit a landing pad for the catchless try which would then
rethrow the exception. The body block still generates a landing
pad that will rethrow its exception, as that is necessary in
the case there are no catch blocks in a function.

Both of these changes are necessary, as just changing the
order of popping control items exposes a bug in catchless
try's with the rethrow erroneously targeting the
catchless try.

This is indeed a UaF, but it's only in code that is preff'ed off by default. So I don't believe this needs to be sec-high. We will land this fix before we pref the feature on.

(In reply to Ryan Hunt [:rhunt] from comment #7)

This is indeed a UaF, but it's only in code that is preff'ed off by default. So I don't believe this needs to be sec-high. We will land this fix before we pref the feature on.

sec-rating is independent of this, but we can mark the feature as "disabled" in current versions, and it won't need sec-approval to land.

Group: javascript-core-security → core-security-release
Status: ASSIGNED → RESOLVED
Closed: 2 years ago
Resolution: --- → FIXED
Target Milestone: --- → 97 Branch
Has Regression Range: --- → yes

As part of a security bug pattern analysis, we are requesting your help with a high level analysis of this bug. It is our hope to develop static analysis (or potentially runtime/dynamic analysis) in the future to identify classes of bugs.

Please visit this google form to reply.

Flags: needinfo?(rhunt)
Whiteboard: [bugmon:update,bisect] → [bugmon:update,bisect][sec-survey]

Done.

Flags: needinfo?(rhunt)
Flags: qe-verify-
Whiteboard: [bugmon:update,bisect][sec-survey] → [bugmon:update,bisect][sec-survey][post-critsmash-triage]
Group: core-security-release
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: