Open Bug 1939970 Opened 1 year ago Updated 9 months ago

Failure to wrap JS exception as wasm with wasm disabled in realm

Categories

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

defect

Tracking

()

People

(Reporter: sm-bugs, Assigned: rhunt)

References

(Blocks 1 open bug)

Details

(Keywords: reporter-external)

Steps to reproduce:

Version: 4f2372638478a8b66ba467867d764ad6cdb16a5d
Args: js --fuzzing-safe --wasm-compiler=none <test-case>
Test case:

a = `
function b() {
    return newGlobal(b)
}
b.principal = WeakMap
c = b()
with (c) {
    function d() {
        e
    }
    l = wasmTextToBinary(\`(module (import "m" "f" (func $f)) (func (export "test") call $f))\`)
    g = WebAssembly.Module
    h = new g(l)
    i = WebAssembly.Instance
    j = {"f": d}
    k = {"m": j}
    new i(h, k).exports.test()
}
`
evalInWorker(a)

Actual results:

    #0 0x55b27fa983d6 in GetOrWrapWasmException(js::jit::JitActivation*, JSContext*) js/src/wasm/WasmBuiltins.cpp:681:3
    #1 0x55b27fa983d6 in js::wasm::HandleExceptionWasm(JSContext*, js::JitFrameIter&, js::jit::ResumeFromException*) js/src/wasm/WasmBuiltins.cpp:816:40
    #2 0x55b27f80b204 in js::jit::HandleException(js::jit::ResumeFromException*) js/src/jit/JitFrames.cpp:754:7
    #3 0x55b27fa9ad58 in WasmHandleThrow(js::jit::ResumeFromException*) js/src/wasm/WasmBuiltins.cpp:914:3
    #4 0x3ba28a46c7c4  (<unknown module>)
Blocks: 1903968
Group: firefox-core-security → core-security
Component: Untriaged → JavaScript: WebAssembly
Product: Firefox → Core
Version: Firefox 133 → Trunk
Group: core-security → javascript-core-security
Assignee: nobody → ydelendik

The program trips on the assert that checks if we are left with OOM exception at the end of the Wasm exception handler. The WasmExceptionObject::wrapJSValue function call returned nullptr because the WebAssembly was not enabled, and that how we ended up at this assert.

The newGlobal during evalInWorker does not respect --wasm-compiler=none option, and creates the WebAssembly namespace anyway, e.g. evalInWorker("newGlobal({principal:WeakMap}).WebAssembly.i") will not fail. The WebAssembly module is compiled and executed inside the worker. That's why the exception ending up in the exception handler. If the assert will be removed, the failure/error will be properly handle anyway -- it is not a security issue in this particular place.

Though, there can be other problems related to evalInWorker("g = newGlobal({principal:WeakMap})"), e.g. new global has to respect options provided in the command line. Jan, can there be other security implication from creating a global without respect of preferences/settings?

Flags: needinfo?(jdemooij)
Severity: -- → S3
Priority: -- → P1

We have special case for system principal to always enable Wasm https://searchfox.org/mozilla-central/source/js/src/wasm/WasmFeatures.cpp#281 . Bug 1576254 enables Wasm for chrome content. Shall we review this policy? Or try to address possible cases with disabled/enabled wasm globals?

Flags: needinfo?(jdemooij) → needinfo?(rhunt)

A reduced test case below.

This probably isn't a security bug - setting wasmExn to nullptr in HandleExceptionWasm just means we don't look for a Wasm exception handler. Also, unlike the browser, the JS shell doesn't use principals consistently and isSystemOrAddonPrincipal always returns true. I suspect this case can't happen in the browser to begin with.

My two cents:

  • The behavior of HandleExceptionWasm and GetOrWrapWasmException isn't clearly defined when multiple realms are involved. IMO that's the root cause and bug 1915254 is a similar bug.
  • I agree with Yury that it's a little surprising that system-or-addon principals override the main is-wasm-enabled pref. For the JS shell we could change --wasm-compiler=none to disable the wasm-for-trusted-principals pref also.
// --wasm-compiler=none
evalInWorker(`
  function throwErr() { throw new Error("hi"); }
  var g = newGlobal({principal:{}});
  var wasm = wasmTextToBinary(\`(module (import "m" "f" (func $f)) (func (export "test") call $f))\`);
  var h = new g.WebAssembly.Module(wasm);
  new g.WebAssembly.Instance(h, {"m": {"f": throwErr}}).exports.test();
`);
See Also: → 1915254

I agree this isn't a security bug, and this is the same root cause of bug 1915254. I have a patch to fix this.

Assignee: ydelendik → rhunt
Group: javascript-core-security
Flags: needinfo?(rhunt)

This turned out to be harder to solve than I hoped. My patches only solved simple cases, but not the general case.

Summary: Assertion failure: cx->isThrowingOutOfMemory(), at js/src/wasm/WasmBuiltins.cpp:681 → Failure to wrap JS exception as wasm with wasm disabled in realm
You need to log in before you can comment on or make changes to this bug.