Assertion failure: wasm::CodeCachingAvailable(cx) [@ WasmCompileAndSerialize] — JS shell drops -P prefs when spawning the --wasm-compile-and-serialize child process
Categories
(Core :: JavaScript: WebAssembly, defect, P3)
Tracking
()
People
(Reporter: bugmon, Unassigned)
References
(Blocks 1 open bug)
Details
(Keywords: ai-involved, assertion, testcase)
Attachments
(4 files)
The SpiderMonkey shell's fuzzing-safe testing function wasmCompileInSeparateProcess() re-executes the shell binary with --wasm-compile-and-serialize to compile a wasm module out of process. The parent gates the call on wasm::CodeCachingAvailable(cx) (js/src/shell/js.cpp:7202), which since lazy tiering became the default returns false unless the shell is run with -P wasm_lazy_tiering=false (js/src/wasm/WasmFeatures.cpp:304; pref default true per modules/libpref/init/StaticPrefList.yaml:10040-10042). When spawning the child, however, the shell only forwards the hardcoded sCompilerProcessFlags list — CPU-feature flags, --test-wasm-await-tier2, and --wasm-compiler= (js/src/shell/js.cpp:7033-7038, 13815-13850). -P prefs are not forwarded. The child therefore runs with lazy tiering enabled, its CodeCachingAvailable check is false, and it immediately fails MOZ_ASSERT(wasm::CodeCachingAvailable(cx)) in WasmCompileAndSerialize (js/src/shell/js.cpp:7151), crashing via MOZ_CrashSequence (reported by UBSan as a null write).
Reproduced independently on a debug shell built from mozconfig.js.linux.debug: running the testcase under --fuzzing-safe -P wasm_lazy_tiering=false yields the identical assertion and stack (WasmCompileAndSerialize <- main) as the researcher's crash data. Without the extra pref the parent gate refuses with 'WebAssembly caching not supported' and no crash occurs, confirming the parent/child pref-mismatch mechanism.
Impact assessment: the crash is a controlled diagnostic assertion in the shell test harness, reachable only through a shell-only testing function; it is not reachable from web content and does not demonstrate memory corruption. Its relevance beyond a harness defect is that in release builds the assert compiles out and the child would run wasm::CompileAndSerialize under a configuration the engine declares unsupported for serialization — the invariants it depends on are enforced downstream only by MOZ_RELEASE_ASSERT(item->inlinedCallerOffsetsMap_.empty()) in js/src/wasm/WasmSerialize.cpp:958 and :982.
Build Info
- Branch: main
- Revision: d6c2474326926e90623514e68fd5eac37fc07269
- Timestamp: 2026-07-26T04:46:02+00:00
Affected Code
File: js/src/shell/js.cpp, line 7150-7151
static bool WasmCompileAndSerialize(JSContext* cx) {
MOZ_ASSERT(wasm::CodeCachingAvailable(cx)); // <-- child crashes here: pref not propagated
File: js/src/shell/js.cpp, line 7200-7205
static bool WasmCompileInSeparateProcess(JSContext* cx, unsigned argc,
Value* vp) {
if (!wasm::CodeCachingAvailable(cx)) { // parent gate passes with -P wasm_lazy_tiering=false
JS_ReportErrorASCII(cx, "WebAssembly caching not supported");
return false;
}
File: js/src/shell/js.cpp, line 13815-13826
// Also the following are to be propagated.
const char* to_propagate[] = {
// Compiler selection options
"--test-wasm-await-tier2",
};
for (const char* p : to_propagate) {
if (op.getBoolOption(p + 2 /* 2 => skip the leading '--' */)) {
if (!sCompilerProcessFlags.append(p)) {
return false;
}
}
}
// (only --wasm-compiler= is additionally reconstituted below; -P prefs are never appended)
File: js/src/wasm/WasmFeatures.cpp, line 296-314
bool wasm::CodeCachingAvailable(JSContext* cx) {
...
// TODO(bug 1913109): lazy tiering doesn't support serialization
if (JS::Prefs::wasm_lazy_tiering()) { // default true -> false in the child
return false;
}
return StreamingCompilationAvailable(cx) && IonAvailable(cx);
}
File: js/src/wasm/WasmSerialize.cpp, line 955-959
MOZ_TRY(CodePodVector(coder, &item->pcOffsets_));
MOZ_TRY(CodePodVector(coder, &item->bytecodeOffsets_));
// Inlining requires lazy tiering, which does not support serialization yet.
MOZ_RELEASE_ASSERT(item->inlinedCallerOffsetsMap_.empty()); // same assert in CodeCallSites at :982
The child-side assertion, the parent-side availability gate, the flag-propagation allowlist that omits -P prefs, the pref-dependent availability check, and the downstream serialization release-asserts the availability gate protects.
Exploit Chain
- Run the SpiderMonkey debug shell with --fuzzing-safe -P wasm_lazy_tiering=false, making wasm::CodeCachingAvailable(cx) true in the parent process (js/src/shell/js.cpp:7202 gate passes).
- Script calls the fuzzing-safe testing function wasmCompileInSeparateProcess(bytecode).
- CompileAndSerializeInSeparateProcess re-executes the same shell binary with --wasm-compile-and-serialize, forwarding only sCompilerProcessFlags (CPU-feature flags, --test-wasm-await-tier2, --wasm-compiler=); the -P wasm_lazy_tiering=false pref is dropped (js/src/shell/js.cpp:7033-7038, 13815-13850).
- The child runs with the default wasm_lazy_tiering=true, so wasm::CodeCachingAvailable(cx) is false there (js/src/wasm/WasmFeatures.cpp:304).
- The child hits MOZ_ASSERT(wasm::CodeCachingAvailable(cx)) in WasmCompileAndSerialize (js/src/shell/js.cpp:7151) and crashes via MOZ_CrashSequence; UBSan reports the deliberate null write. In a release build the assert compiles out and the child proceeds to serialize under a configuration the engine marks unsupported, guarded downstream only by the MOZ_RELEASE_ASSERTs in js/src/wasm/WasmSerialize.cpp:958/982.
Steps to Reproduce
- Build the SpiderMonkey debug shell: build_firefox with mozconfig /app/mozconfigs/mozconfig.js.linux.debug (build_dir /firefox/obj-js-debug).
- Run the testcase via js_shell_evaluator with js_binary /firefox/obj-js-debug/dist/bin/js and flags ["-P", "wasm_lazy_tiering=false"] (the evaluator always adds --fuzzing-safe).
- Testcase: const bin = wasmTextToBinary(
(module (memory 1) (func (export "run") (param i32) (result i32) (i32.add (local.get 0) (i32.const 1))))); print("caching: " + wasmCachingEnabled()); const mod = wasmCompileInSeparateProcess(bin); print(wasmLoadedFromCache(mod)); - Observe crashed: true with 'Assertion failure: wasm::CodeCachingAvailable(cx), at js/src/shell/js.cpp:7151' from the spawned --wasm-compile-and-serialize child on stderr. Without the -P flag, the call instead fails cleanly with 'WebAssembly caching not supported' and no crash.
Security Impact
- Severity: Low
- Attacker capability: Deterministic assertion crash of the SpiderMonkey shell's out-of-process wasm compiler child. The trigger is a shell-only testing function (wasmCompileInSeparateProcess); no memory corruption was demonstrated, and no path from web content exists. In release shell builds the compiled-out assert means the child serializes wasm code under a configuration the engine declares unsupported for serialization, with the WasmSerialize.cpp MOZ_RELEASE_ASSERTs (lines 958, 982) as the remaining guard.
- Preconditions: SpiderMonkey shell with testing functions available (runs under --fuzzing-safe); shell invoked with -P wasm_lazy_tiering=false so the parent-side CodeCachingAvailable gate passes while the pref is dropped for the child.
ASAN Report
[954291] Assertion failure: wasm::CodeCachingAvailable(cx), at js/src/shell/js.cpp:7151
UndefinedBehaviorSanitizer:DEADLYSIGNAL
==954291==ERROR: UndefinedBehaviorSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5c920be6418f bp 0x7ffe7ab6a5d0 sp 0x7ffe7ab6a550 T954291)
==954291==The signal is caused by a WRITE memory access.
==954291==Hint: address points to the zero page.
#0 0x5c920be6418f in MOZ_CrashSequence(void*, long) /firefox/obj-js-debug/dist/include/mozilla/Assertions.h:261:3
#1 0x5c920be6418f in WasmCompileAndSerialize(JSContext*) /firefox/obj-js-debug/js/src/shell/./../../../../js/src/shell/js.cpp:7151:3
#2 0x5c920be5d5a3 in main /firefox/obj-js-debug/js/src/shell/./../../../../js/src/shell/js.cpp:12799:10
#3 0x7de815cfb1c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9)
#4 0x7de815cfb28a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a)
#5 0x5c920be23578 in _start (/firefox/obj-js-debug/dist/bin/js+0x270e578)
SUMMARY: UndefinedBehaviorSanitizer: SEGV /firefox/obj-js-debug/dist/include/mozilla/Assertions.h:261:3 in MOZ_CrashSequence(void*, long)
==954291==ABORTING
| Reporter | ||
Comment 1•3 months ago
|
||
| Reporter | ||
Comment 2•3 months ago
|
||
| Reporter | ||
Comment 3•3 months ago
|
||
| Reporter | ||
Comment 4•3 months ago
|
||
Comment 5•3 months ago
|
||
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 crash is real (confirmed by ASAN), but the root cause analysis and suggested fix may contain errors.
WARNING: A blocking issue was auto-patched in the source used for this analysis. Please flag if you believe the bug was introduced by that patch.
Updated•3 months ago
|
Updated•3 months ago
|
Updated•2 months ago
|
Description
•