Closed Bug 1761606 Opened 3 years ago Closed 3 years ago

AddressSanitizer: heap-buffer-overflow [@ js::wasm::Instance::setDebugFilter] with READ of size 4

Categories

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

x86_64
Linux
defect

Tracking

()

RESOLVED FIXED
100 Branch
Tracking Status
firefox-esr91 --- unaffected
firefox98 --- unaffected
firefox99 --- unaffected
firefox100 + fixed

People

(Reporter: decoder, Assigned: rhunt)

References

(Regression)

Details

(Keywords: crash, regression, testcase, Whiteboard: [bugmon:update,bisect][fuzzblocker])

Crash Data

Attachments

(3 files)

The following testcase crashes on mozilla-central revision 20220325-b94d53ba65d2 (asan-opt build, run with --fuzzing-safe --no-threads):

try {
  evaluate(`
  var g76 = newGlobal({newCompartment: true});
  g76.parent = this;
  g76.eval("Debugger(parent).onExceptionUnwind = function () {};");
  function wasmEvalText(str, imports, options) {
    let binary = wasmTextToBinary(str);
    m = new WebAssembly.Module(binary, options);
    return new WebAssembly.Instance(m, imports);
  }
  var table = wasmEvalText(\`(module
        (func \$add0 (param i32) (result i32) (i32.add (get_local 0) (i32.const 0)))
        (func \$add1 (param i32) (result i32) (i32.add (get_local 0) (i32.const 1)))
        (func \$add3 (param i32) (result i32) (i32.add (get_local 0) (i32.const 3)))
        (func \$add4 (param i32) (result i32) (i32.add (get_local 0) (i32.const 4)))
        (func \$add9 (param i32) (result i32) (i32.add (get_local 0) (i32.const 9)))
    )\`).exports.table;
  class C0 {
    async *method([] = [[]]) {
      assert.sameValue(callCount, 0);
    }
  };
  new C0().method().next().then(() => {}).then();
`);
} catch(exc) {}

Backtrace:

==25231==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000075f4 at pc 0x55f447c4a4a7 bp 0x7ffd74937fa0 sp 0x7ffd74937f98
READ of size 4 at 0x6020000075f4 thread T0
    #0 0x55f447c4a4a6 in js::wasm::Instance::setDebugFilter(unsigned int, bool) js/src/wasm/WasmInstance.cpp:1638:48
    #1 0x55f447c0cdbf in enableDebuggingForFunction js/src/wasm/WasmDebug.cpp:290:13
    #2 0x55f447c0cdbf in js::wasm::DebugState::adjustEnterAndLeaveFrameTrapsState(JSContext*, js::wasm::Instance*, bool) js/src/wasm/WasmDebug.cpp:323:7
    #3 0x55f447c0d47f in js::wasm::DebugState::ensureEnterFrameTrapsState(JSContext*, js::wasm::Instance*, bool) js/src/wasm/WasmDebug.cpp:365:3
    #4 0x55f446ed6366 in UpdateExecutionObservabilityOfScriptsInZone(JSContext*, JS::Zone*, js::DebugAPI::ExecutionObservableSet const&, js::DebugAPI::IsObserving) js/src/debugger/Debugger.cpp:3262:25
    #5 0x55f446ed543e in js::Debugger::updateExecutionObservabilityOfScripts(JSContext*, js::DebugAPI::ExecutionObservableSet const&, js::DebugAPI::IsObserving) js/src/debugger/Debugger.cpp:3274:12
    #6 0x55f446ed68f5 in js::Debugger::updateExecutionObservability(JSContext*, js::DebugAPI::ExecutionObservableSet&, js::DebugAPI::IsObserving) js/src/debugger/Debugger.cpp:3348:10
    #7 0x55f446ed6b1f in js::Debugger::ensureExecutionObservabilityOfScript(JSContext*, JSScript*) js/src/debugger/Debugger.cpp:3359:10
    #8 0x55f446f18299 in js::DebuggerFrame::setGeneratorInfo(JSContext*, JS::Handle<js::DebuggerFrame*>, JS::Handle<js::AbstractGeneratorObject*>) js/src/debugger/Frame.cpp:391:8
    #9 0x55f446eb782d in js::DebuggerFrame::create(JSContext*, JS::Handle<JSObject*>, JS::Handle<js::NativeObject*>, js::FrameIter const*, JS::Handle<js::AbstractGeneratorObject*>) js/src/debugger/Frame.cpp:257:10
    #10 0x55f446eb6785 in js::Debugger::getFrame(JSContext*, js::FrameIter const&, JS::MutableHandle<js::DebuggerFrame*>) js/src/debugger/Debugger.cpp:684:13
    #11 0x55f446eb5b05 in js::Debugger::getFrame(JSContext*, js::FrameIter const&, JS::MutableHandle<JS::Value>) js/src/debugger/Debugger.cpp:617:18
    #12 0x55f446ec8d6b in js::Debugger::fireExceptionUnwind(JSContext*, JS::Handle<JS::Value>, js::ResumeMode&, JS::MutableHandle<JS::Value>) js/src/debugger/Debugger.cpp:2260:8
    #13 0x55f446ec1613 in operator() js/src/debugger/Debugger.cpp:1350:21
    #14 0x55f446ec1613 in operator() js/src/debugger/Debugger.cpp:876:32
    #15 0x55f446ec1613 in operator() js/src/debugger/Debugger.cpp:844:61
    #16 0x55f446ec1613 in enterDebuggerHook<(lambda at js/src/debugger/Debugger.cpp:844:38)> js/src/debugger/Debugger.h:1019:10
    #17 0x55f446ec1613 in dispatchHook<(lambda at js/src/debugger/Debugger.cpp:875:23)> js/src/debugger/Debugger.cpp:844:16
    #18 0x55f446ec1613 in dispatchResumptionHook<(lambda at js/src/debugger/Debugger.cpp:1348:7)> js/src/debugger/Debugger.cpp:874:10
    #19 0x55f446ec1613 in js::DebugAPI::slowPathOnExceptionUnwind(JSContext*, js::AbstractFramePtr) js/src/debugger/Debugger.cpp:1346:30
    #20 0x55f446687ce5 in onExceptionUnwind js/src/debugger/DebugAPI-inl.h:143:12
    #21 0x55f446687ce5 in HandleError js/src/vm/Interpreter.cpp:1239:12
    #22 0x55f446687ce5 in Interpret(JSContext*, js::RunState&) js/src/vm/Interpreter.cpp:4554:11
    #23 0x55f446665cc1 in js::RunScript(JSContext*, js::RunState&) js/src/vm/Interpreter.cpp:394:13
    #24 0x55f4466943df in js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct, js::CallReason) js/src/vm/Interpreter.cpp:544:13
    #25 0x55f44669604b in js::Call(JSContext*, JS::Handle<JS::Value>, JS::Handle<JS::Value>, js::AnyInvokeArgs const&, JS::MutableHandle<JS::Value>, js::CallReason) js/src/vm/Interpreter.cpp:589:8
    #26 0x55f446b88fa7 in js::CallSelfHostedFunction(JSContext*, JS::Handle<js::PropertyName*>, JS::Handle<JS::Value>, js::AnyInvokeArgs const&, JS::MutableHandle<JS::Value>) js/src/vm/SelfHosting.cpp:1586:10
    #27 0x55f4467e2c37 in AsyncGeneratorResume(JSContext*, JS::Handle<js::AsyncGeneratorObject*>, js::CompletionKind, JS::Handle<JS::Value>) js/src/vm/AsyncIteration.cpp:1075:8
    #28 0x55f4467e1a43 in js::AsyncGeneratorNext(JSContext*, unsigned int, JS::Value*) js/src/vm/AsyncIteration.cpp:858:12
    #29 0x55f4466942a4 in CallJSNative js/src/vm/Interpreter.cpp:425:13
    #30 0x55f4466942a4 in js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct, js::CallReason) js/src/vm/Interpreter.cpp:512:12
    #31 0x55f446680ac3 in CallFromStack js/src/vm/Interpreter.cpp:576:10
    #32 0x55f446680ac3 in Interpret(JSContext*, js::RunState&) js/src/vm/Interpreter.cpp:3302:16
    #33 0x55f446665cc1 in js::RunScript(JSContext*, js::RunState&) js/src/vm/Interpreter.cpp:394:13
    #34 0x55f446697e63 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
    #35 0x55f44684062b in ExecuteScript(JSContext*, JS::Handle<JSObject*>, JS::Handle<JSScript*>, JS::MutableHandle<JS::Value>) js/src/vm/CompilationAndEvaluation.cpp:515:10
    #36 0x55f446840310 in JS_ExecuteScript(JSContext*, JS::Handle<JSScript*>, JS::MutableHandle<JS::Value>) js/src/vm/CompilationAndEvaluation.cpp:532:10
    #37 0x55f44634c1e3 in Evaluate(JSContext*, unsigned int, JS::Value*) js/src/shell/js.cpp:2460:19
    #38 0x55f4466942a4 in CallJSNative js/src/vm/Interpreter.cpp:425:13
    [...]
    #50 0x55f44630f763 in main js/src/shell/js.cpp:12735:12

0x6020000075f4 is located 0 bytes to the right of 4-byte region [0x6020000075f0,0x6020000075f4)
allocated by thread T0 here:
    #0 0x55f4462c6b72 in __interceptor_calloc /builds/worker/fetches/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:138:3
    #1 0x55f447c46a93 in js_arena_calloc /builds/worker/workspace/obj-build/dist/include/js/Utility.h:383:10
    #2 0x55f447c46a93 in js_calloc /builds/worker/workspace/obj-build/dist/include/js/Utility.h:391:10
    #3 0x55f447c46a93 in js::wasm::Instance::init(JSContext*, JS::GCVector<JSFunction*, 0ul, js::SystemAllocPolicy> const&, JS::GCVector<js::wasm::Val, 0ul, js::SystemAllocPolicy> const&, JS::GCVector<js::WasmGlobalObject*, 0ul, js::SystemAllocPolicy> const&, JS::GCVector<js::WasmTagObject*, 0ul, js::SystemAllocPolicy> const&, mozilla::Vector<js::wasm::SerializableRefPtr<js::wasm::DataSegment const>, 0ul, js::SystemAllocPolicy> const&, mozilla::Vector<js::wasm::SerializableRefPtr<js::wasm::ElemSegment const>, 0ul, js::SystemAllocPolicy> const&) js/src/wasm/WasmInstance.cpp:1437:31
    #4 0x55f447c95da4 in js::WasmInstanceObject::create(JSContext*, RefPtr<js::wasm::Code const>, mozilla::Vector<js::wasm::SerializableRefPtr<js::wasm::DataSegment const>, 0ul, js::SystemAllocPolicy> const&, mozilla::Vector<js::wasm::SerializableRefPtr<js::wasm::ElemSegment const>, 0ul, js::SystemAllocPolicy> const&, unsigned int, JS::Handle<js::WasmMemoryObject*>, mozilla::Vector<RefPtr<js::wasm::Table>, 0ul, js::SystemAllocPolicy>&&, JS::GCVector<JSFunction*, 0ul, js::SystemAllocPolicy> const&, mozilla::Vector<js::wasm::GlobalDesc, 0ul, js::SystemAllocPolicy> const&, JS::GCVector<js::wasm::Val, 0ul, js::SystemAllocPolicy> const&, JS::GCVector<js::WasmGlobalObject*, 0ul, js::SystemAllocPolicy> const&, JS::GCVector<js::WasmTagObject*, 0ul, js::SystemAllocPolicy> const&, JS::Handle<JSObject*>, mozilla::UniquePtr<js::wasm::DebugState, JS::DeletePolicy<js::wasm::DebugState> >) js/src/wasm/WasmJS.cpp:2062:18
    #5 0x55f447c86610 in js::wasm::Module::instantiate(JSContext*, js::wasm::ImportValues&, JS::Handle<JSObject*>, JS::MutableHandle<js::WasmInstanceObject*>) const js/src/wasm/WasmModule.cpp:1207:16
    #6 0x55f447c9708b in js::WasmInstanceObject::construct(JSContext*, unsigned int, JS::Value*) js/src/wasm/WasmJS.cpp:2125:16
    #7 0x55f44669681f in CallJSNative js/src/vm/Interpreter.cpp:425:13
    #8 0x55f44669681f in CallJSNativeConstructor js/src/vm/Interpreter.cpp:441:8
    #9 0x55f44669681f in InternalConstruct(JSContext*, js::AnyConstructArgs const&) js/src/vm/Interpreter.cpp:617:14
    #10 0x55f446680a74 in Interpret(JSContext*, js::RunState&) js/src/vm/Interpreter.cpp:3292:16
    #11 0x55f446665cc1 in js::RunScript(JSContext*, js::RunState&) js/src/vm/Interpreter.cpp:394:13
    #12 0x55f446697e63 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
    #13 0x55f44684062b in ExecuteScript(JSContext*, JS::Handle<JSObject*>, JS::Handle<JSScript*>, JS::MutableHandle<JS::Value>) js/src/vm/CompilationAndEvaluation.cpp:515:10
    #14 0x55f446840310 in JS_ExecuteScript(JSContext*, JS::Handle<JSScript*>, JS::MutableHandle<JS::Value>) js/src/vm/CompilationAndEvaluation.cpp:532:10
    #15 0x55f44634c1e3 in Evaluate(JSContext*, unsigned int, JS::Value*) js/src/shell/js.cpp:2460:19
    #16 0x55f4466942a4 in CallJSNative js/src/vm/Interpreter.cpp:425:13
    [...]
    #28 0x55f44630f763 in main js/src/shell/js.cpp:12735:12

SUMMARY: AddressSanitizer: heap-buffer-overflow js/src/wasm/WasmInstance.cpp:1638:48 in js::wasm::Instance::setDebugFilter(unsigned int, bool)
Shadow bytes around the buggy address:
  0x0c047fff8e90: fa fa 00 04 fa fa 00 04 fa fa fd fd fa fa fd fd
  0x0c047fff8ea0: fa fa fd fa fa fa fd fd fa fa fd fd fa fa fd fa
=>0x0c047fff8eb0: fa fa 04 fa fa fa 00 fa fa fa 00 fa fa fa[04]fa
  0x0c047fff8ec0: fa fa 00 fa fa fa 00 fa fa fa fd fa fa fa fd fa
  0x0c047fff8ed0: fa fa fd fd fa fa fd fa fa fa 00 00 fa fa fd fd
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

Marking s-s due to heap-buffer-overflow, but this seems to require the Debugger, so might be moderate. Also, this happens very frequently, marking as fuzzblocker. It also produces unreliable crashes outside of ASan.

Attached file Testcase

This needs to be addressed asap: We're seeing all sorts of crashes related to this that don't have the Debugger on the stack. They all look like sec-bugs and there is no way for us to distinguish them from real sec-bugs.

Flags: needinfo?(lhansen)

setDebugFilter divides funcIndex by 4, when it should divide by 32.
Baseline compiler does it correctly.
This commit also reworks the calculation of the filter array length
to make it (hopefully) easier to follow. An assert that the correct
instance is being used in DebugState is also added, but may be
unnecessary.

Assignee: nobody → rhunt
Status: NEW → ASSIGNED

setDebugFilter() accesses a 32-bit granularity bit flag vector indexed by funcindex. We allocate it correctly, but the accessor here divides by 4 (sizeof(uint32_t)) when it should divide by 32. This translates to a write out of bounds.

The amount of functions is user controlled, but the debugger is not. So I'm unsure if this exploitable. The writes will always be all-1's or all-0's, and not user controlled.

Bugmon Analysis
Unable to reproduce bug 1761606 using build mozilla-central 20220325093814-b94d53ba65d2. 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
See Also: → 1760627
Flags: needinfo?(lhansen)
Priority: -- → P1
Attachment #9269549 - Attachment description: Bug 1761606 - wasm: Adjust address calculation in setDebugFilter. r?lth → Bug 1761606 - wasm: Clean up debug filter allocation. r?lth
Regressed by: 1756951

Set release status flags based on info from the regressing bug 1756951

Group: javascript-core-security → core-security-release
Status: ASSIGNED → RESOLVED
Closed: 3 years ago
Resolution: --- → FIXED
Target Milestone: --- → 100 Branch
Has Regression Range: --- → yes
Crash Signature: [@ js::wasm::DebugState::adjustEnterAndLeaveFrameTrapsState]
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: