Open Bug 2068370 Opened 2 months ago Updated 2 months ago

Assertion failure: hasPrototype(key) in [@ js::wasm::Module::instantiateMemories] via cross-realm WebAssembly.Instance constructor

Categories

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

defect

Tracking

()

UNCONFIRMED

People

(Reporter: bugmon, Unassigned)

References

(Blocks 1 open bug)

Details

(Keywords: assertion, testcase)

Attachments

(4 files)

js::wasm::Module::instantiateMemories (WasmModule.cpp:540) fetches the exported-object prototypes with the non-creating GlobalObject::getPrototype(JSProto_WasmMemory), which requires the prototype to already exist in cx->realm(); because the wasm classes are DontDefineConstructor and GetPrototypeFromConstructor's cross-realm fallback resolves only JSProto_WasmInstance (linking proto.constructor to that realm's Instance constructor), script can reach WasmInstanceObject::construct in a realm where WasmMemory/Table/Global/Tag were never resolved. Calling Object.getPrototypeOf(Reflect.construct(WebAssembly.Instance, [mod], ntWithNonObjectPrototype)).constructor then hits Assertion failure: hasPrototype(key) at GlobalObject.h:421 in debug builds; in release the assert is compiled out and a null prototype is handed to WasmMemoryObject::create.

Exploit Chain

  1. Create a second realm g with newGlobal() and never touch g.WebAssembly, so none of the wasm classes are resolved there.
  2. Create nt = g.Function() and set nt.prototype = null so GetPrototypeFromConstructor takes the cross-realm fallback branch.
  3. Compile a module exporting a memory and call Reflect.construct(WebAssembly.Instance, [mod], nt); GetPrototypeFromConstructor enters realm g and calls getOrCreatePrototype(JSProto_WasmInstance), resolving only WasmInstance there and linking proto.constructor to g's Instance constructor.
  4. Read Object.getPrototypeOf(inst).constructor to obtain realm g's WebAssembly.Instance constructor (verified distinct from the local one and an instance of g.Function).
  5. Call new GInstance(mod); WasmInstanceObject::construct runs Module::instantiate in realm g, and instantiateMemories calls getPrototype(JSProto_WasmMemory) on a realm where that prototype was never created, failing MOZ_ASSERT(hasPrototype(key)) at GlobalObject.h:421.

Affected Code

Steps to Reproduce

  1. Build the SpiderMonkey debug shell with /app/mozconfigs/mozconfig.js.linux.debug from an unmodified tree (build_dir /firefox/obj-js-debug).
  2. Run wasm_instance_unresolved_realm.js with js_shell_evaluator against /firefox/obj-js-debug/dist/bin/js (default --fuzzing-safe, no extra flags, no prefs, no patches).
  3. Observe Assertion failure: hasPrototype(key), at /firefox/js/src/vm/GlobalObject.h:421 with js::wasm::Module::instantiateMemories (WasmModule.cpp:540) on the stack.

Security Impact

  • Severity: Low
  • Attacker capability: Script can obtain a realm's WebAssembly.Instance constructor without that realm's other wasm prototypes existing and drive Module::instantiate into a state the engine asserts is impossible. In debug builds this is an assertion abort; from the source, release builds would create exported WasmMemory/Table/Global/Tag objects with a null pointer as their prototype. No memory corruption was demonstrated.
  • Preconditions: Access to two realms (newGlobal() in the shell; cross-origin/same-origin frames would be the browser analogue, not tested here) and the ability to call Reflect.construct with a foreign-realm newTarget whose .prototype is a non-object.

The full analysis report is attached (analysis.md).

Attached file crash_stack.txt —
Attached patch fix.patch — — Splinter Review
Attached file analysis.md —

This bug was filed automatically by an AI-powered security testing tool. The analysis, testcase, and any suggested patches were generated by an AI model and have NOT been verified by a human.

Please review all details carefully before triaging. The issue was confirmed by an independent verifier, but for non-ASAN issues, the verifier has been made less strict. Additionally, the root cause analysis and suggested fix may contain errors.

Group: core-security → javascript-core-security
Blocks: sm-security
Severity: -- → S3
Priority: -- → P3

In release builds this results in an object with null proto.

Group: javascript-core-security
Keywords: sec-other
Component: JavaScript Engine → JavaScript: WebAssembly

The component has been changed since the backlog priority was decided, so we're resetting it.
For more information, please visit BugBot documentation.

Priority: P3 → --
Priority: -- → P3
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: