Open Bug 2073688 Opened 1 month ago Updated 1 month ago

ResetFallbackStubStates leaves Ion bailout metadata inconsistent

Categories

(Core :: JavaScript Engine: JIT, defect, P3)

defect

Tracking

()

UNCONFIRMED

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

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

  1. Warm outer(leaf, 1) until optimized execution uses inlining metadata for leaf.
  2. While that invocation remains active, call resetFallbackStubStates(outer) from tick.
  3. Reenter outer with other 2,000 times, rebuilding the caller's inlining metadata for another callee.
  4. During invalidation bailout, select the replacement metadata for the older frame and abort at the callee IC-entry-count release assertion.

Steps to Reproduce

  1. Use unmodified Firefox revision 383240b1c47fcbe804430e196a78f57abf4e1dfb.
  2. Run build_firefox with firefox_dir=/firefox and mozconfig_path=/app/mozconfigs/mozconfig.js.linux.debug.
  3. 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.
  4. 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.
  5. 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
Attached patch fix.patch — — Splinter Review
Attached file crash_stack.txt —
Attached file testcase.js —
Attached file crashdata.txt —

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

Testing function issue.

No longer blocks: sm-security
Group: javascript-core-security
Keywords: sec-other
Component: JavaScript: Standard Library → JavaScript Engine: JIT

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 → --
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: