ResetFallbackStubStates leaves Ion bailout metadata inconsistent
Categories
(Core :: JavaScript Engine: JIT, defect, P3)
Tracking
()
People
(Reporter: bugmon, Unassigned)
References
Details
(Keywords: assertion, testcase)
Attachments
(4 files)
ResetFallbackStubStates removes trial-inlined child mappings without marking active IonScripts as having purged ICScripts. Reentrant calls can replace those mappings while an older optimized frame remains active. BaselineStackBuilder::setNextCallee then selects incompatible callee metadata and fails its numICEntries release assertion.
A JavaScript author with access to SpiderMonkey's shell testing API can abort the shell by resetting an active optimized caller's IC state and reentering it with another callee. The testcase demonstrates this under --fuzzing-safe. Memory corruption and web-content reachability were not demonstrated.
This finding was discovered during variant analysis seeded by bug 2013583.
Build Info
- Branch: main
- Revision: 383240b1c47fcbe804430e196a78f57abf4e1dfb
- Timestamp: 2026-09-18T04:20:17+00:00
Affected Code
File: js/src/builtin/TestingFunctions.cpp, line 9957-9963
if (icScript->hasInlinedChild(stub->pcOffset())) {
icScript->removeInlinedChild(stub->pcOffset());
}
stub->state().reset();
}
script->jitScript()->notePurgedStubs();
File: js/src/jit/JitScript.h, line 630-635
inline void notePurgedStubs() {
#ifdef DEBUG
failedICHash_.reset();
hasPurgedStubs_ = true;
#endif
}
File: js/src/jit/BaselineBailouts.cpp, line 544-548
if (trialInliningState == TrialInliningState::Inlined &&
!iter_.ionScript()->purgedICScripts() && canUseTrialInlinedICScripts_) {
// Update icScript_ to point to the icScript of nextCallee
const uint32_t pcOff = script_->pcToOffset(pc_);
icScript_ = icScript_->findInlinedChild(pcOff);
File: js/src/jit/BaselineBailouts.cpp, line 567-570
// Assert the ICScript matches nextCallee.
JSScript* calleeScript = nextCallee->nonLazyScript();
MOZ_RELEASE_ASSERT(icScript_->numICEntries() == calleeScript->numICEntries());
MOZ_RELEASE_ASSERT(icScript_->bytecodeSize() == calleeScript->length());
The reset helper changes the inlining graph but updates only debug bookkeeping. Bailout reconstruction continues using that graph unless the active IonScript is marked as having purged ICScripts. The normal GC path performs that marking at js/src/jit/JitScript.cpp:845-847.
Exploit Chain
- Warm outer(leaf, 1) until optimized execution uses inlining metadata for leaf.
- While that invocation remains active, call resetFallbackStubStates(outer) from tick.
- Reenter outer with other 2,000 times, rebuilding the caller's inlining metadata for another callee.
- During invalidation bailout, select the replacement metadata for the older frame and abort at the callee IC-entry-count release assertion.
Steps to Reproduce
- Use unmodified Firefox revision 383240b1c47fcbe804430e196a78f57abf4e1dfb.
- Run build_firefox with firefox_dir=/firefox and mozconfig_path=/app/mozconfigs/mozconfig.js.linux.debug.
- Pass the contents of /tmp/tmpp1g5f0z3/testcase.js to js_shell_evaluator with js_binary=/firefox/obj-js-debug/dist/bin/js, flags=[], and timeout=30. The evaluator supplies --fuzzing-safe; the testcase sets offthread-compilation.enable=0.
- Observe crashed=true, timed_out=false, exit_code=1 and the assertion at BaselineBailouts.cpp:569. The independently captured log is /app/output/verification-tmpp1g5f0z3/runs/reproduction/stderr-0.txt.
- For causal controls, remove the reset, supply --no-ion, or insert gc(undefined, "shrinking") immediately after the reset. Each independently tested control exits successfully.
Security Impact
- Severity: Low
- Attacker capability: Trigger a release assertion that terminates the SpiderMonkey shell. No memory corruption, arbitrary execution, or browser-content impact was demonstrated.
- Preconditions: JavaScript execution with access to the resetFallbackStubStates shell testing API and Ion enabled. The submitted reproduction disables off-thread compilation; no source patches, browser preferences, or external services are required.
ASAN Report
Debug assertion/UBSan output; this is not an ASan memory-corruption report:
[6651] Assertion failure: icScript_->numICEntries() == calleeScript->numICEntries(), at js/src/jit/BaselineBailouts.cpp:569
UndefinedBehaviorSanitizer:DEADLYSIGNAL
==6651==ERROR: UndefinedBehaviorSanitizer: SEGV on unknown address 0x000000000000 (pc 0x55fd8b6aa85b bp 0x7eb327134120 sp 0x7eb327134100 T6651)
#0 0x55fd8b6aa85b in MOZ_CrashSequence(void*, long) /firefox/obj-js-debug/dist/include/mozilla/Assertions.h:261:3
#1 0x55fd8b6aa85b in BaselineStackBuilder::setNextCallee(JSFunction*, js::jit::TrialInliningState) /firefox/obj-js-debug/js/src/jit/./../../../../js/src/jit/BaselineBailouts.cpp:569:3
#2 0x55fd8b6afcef in BaselineStackBuilder::buildStubFrame(unsigned int, JS::Handle<JS::StackGCVector<JS::Value, js::TempAllocPolicy>>) /firefox/obj-js-debug/js/src/jit/./../../../../js/src/jit/BaselineBailouts.cpp:1199:3
#3 0x55fd8b6b38e5 in BaselineStackBuilder::buildOneFrame() /firefox/obj-js-debug/js/src/jit/./../../../../js/src/jit/BaselineBailouts.cpp:1570:10
#4 0x55fd8b6a7b36 in js::jit::BailoutIonToBaseline(JSContext*, js::jit::JitActivation*, js::jit::JSJitFrameIter const&, js::jit::BaselineBailoutInfo**, js::jit::ExceptionBailoutInfo const*, js::jit::BailoutReason) /firefox/obj-js-debug/js/src/jit/./../../../../js/src/jit/BaselineBailouts.cpp:1707:18
#5 0x55fd8b6a8aa3 in js::jit::InvalidationBailout(js::jit::InvalidationBailoutStack*, js::jit::BaselineBailoutInfo**) /firefox/obj-js-debug/js/src/jit/./../../../../js/src/jit/Bailouts.cpp:220:18
SUMMARY: UndefinedBehaviorSanitizer: SEGV /firefox/obj-js-debug/dist/include/mozilla/Assertions.h:261:3 in MOZ_CrashSequence(void*, long)
==6651==ABORTING
| Reporter | ||
Comment 1•1 month ago
|
||
| Reporter | ||
Comment 2•1 month ago
|
||
| Reporter | ||
Comment 3•1 month ago
|
||
| Reporter | ||
Comment 4•1 month ago
|
||
Comment 5•1 month 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 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.
Updated•1 month ago
|
Updated•1 month ago
|
Updated•1 month ago
|
Comment 6•1 month ago
|
||
Testing function issue.
Updated•1 month ago
|
Comment 7•1 month ago
|
||
The component has been changed since the backlog priority was decided, so we're resetting it.
For more information, please visit BugBot documentation.
Updated•1 month ago
|
Description
•