Closed Bug 2076003 Opened 1 month ago Closed 1 month ago

Large function with a trailing empty switch crashes debug SpiderMonkey

Categories

(Core :: JavaScript Engine, defect, P3)

defect

Tracking

()

RESOLVED FIXED
159 Branch
Tracking Status
firefox-esr115 --- wontfix
firefox-esr153 --- wontfix
firefox157 --- wontfix
firefox158 --- wontfix
firefox159 --- fixed

People

(Reporter: bugmon, Assigned: iain)

References

(Blocks 1 open bug)

Details

(Keywords: assertion, testcase)

Attachments

(3 files, 1 external file)

BytecodeEmitter::allocateTableSwitchResumeIndexRange assigns firstResumeIndex from numCaseOffsets(), but checks the resume-index limit only while appending cases (js/src/frontend/BytecodeEmitter.cpp:2177). After 16,777,216 entries, an empty switch receives 0x1000000, and SwitchEmitter::emitEnd passes it to SET_RESUMEINDEX, violating the 24-bit SET_UINT24 assertion (js/src/frontend/SwitchEmitter.cpp:386; js/src/vm/BytecodeUtil.h:89).

A JavaScript author can crash a debug SpiderMonkey shell by compiling a sufficiently large function ending in an empty switch. The unchanged --fuzzing-safe debug shell reproduces the assertion, while the optimized ASAN shell completes the same testcase. Release-build memory corruption is not demonstrated.

This finding was discovered during variant analysis seeded by bug 1827359.

Build Info

Affected Code

File: js/src/frontend/BytecodeEmitter.cpp, line 2177

  *firstResumeIndex = list.numCaseOffsets();
  if (!list.appendTableSwitch(switchOffset)) {
    return false;
  }

  for (BytecodeOffset offset : caseOffsets) {
    if (!checkResumeIndexLimit()) {
      return false;
    }
    if (!list.appendCaseOffset(offset)) {
      return false;
    }
  }

File: js/src/frontend/SwitchEmitter.cpp, line 382

    if (!bce_->allocateTableSwitchResumeIndexRange(offsets, top_,
                                                   &firstResumeIndex)) {
      return false;
    }
    SET_RESUMEINDEX(pc, firstResumeIndex);

File: js/src/vm/BytecodeUtil.h, line 88

static MOZ_ALWAYS_INLINE void SET_UINT24(jsbytecode* pc, uint32_t i) {
  MOZ_ASSERT(i < (1 << 24));

MaxResumeIndex is the 24-bit mask (js/src/frontend/BytecodeEmitter.cpp:2138). The limit check runs before insertion, allowing a table to contain 2^24 entries (js/src/frontend/BytecodeEmitter.cpp:2150). An empty caseOffsets span bypasses that check and returns the one-past-end count as an index. Removing the final empty switch or retaining it with one fewer preceding entry makes the debug testcase succeed.

Exploit Chain

  1. Generate 512 switches spanning integer cases 0 through 32766, followed by a switch spanning 0 through 511; their jump tables require 16,777,216 resume entries.
  2. Append an empty switch and pass the generated body to the ordinary Function constructor.
  3. Compilation passes the unrepresentable resume index to SET_UINT24 and aborts the debug engine.

Steps to Reproduce

  1. Use unmodified Firefox revision c32abda0190531351e22da36334f18f9e994d474. Run build_firefox with firefox_dir=/firefox and mozconfig_path=/app/mozconfigs/mozconfig.js.linux.debug.
  2. Run js_shell_evaluator with the contents of /app/output/verification-tmpfru_7e7y/resume-index-overflow.js, js_binary=/firefox/obj-js-debug/dist/bin/js, flags=[], and timeout=240.
  3. Observe crashed=true, timed_out=false, and exit_code=1. The complete independent stack is saved at /app/output/verification-tmpfru_7e7y/debug-crash.stderr.txt:1. Build and control results are recorded in /app/output/verification-tmpfru_7e7y/verification.json:1.

Security Impact

  • Severity: Low
  • Attacker capability: Abort an assertions-enabled SpiderMonkey process during JavaScript compilation. Release-build memory corruption or code execution was not demonstrated.
  • Preconditions: Ability to supply JavaScript to the Function constructor and sufficient resources to compile 16,777,216 table-switch resume entries. The demonstrated abort requires the debug build.

ASAN Report

[13572] Assertion failure: i < (1 << 24), at /firefox/js/src/vm/BytecodeUtil.h:89
    #0 0x5605929ea3ce in MOZ_CrashSequence(void*, long) /firefox/obj-js-debug/dist/include/mozilla/Assertions.h:261:3
    #1 0x5605929ea3ce in SET_UINT24(unsigned char*, unsigned int) /firefox/js/src/vm/BytecodeUtil.h:89:3
    #2 0x5605929ea3ce in SET_RESUMEINDEX(unsigned char*, unsigned int) /firefox/js/src/vm/BytecodeUtil.h:195:3
    #3 0x5605929ea3ce in js::frontend::SwitchEmitter::emitEnd() /firefox/obj-js-debug/js/src/frontend/./../../../../js/src/frontend/SwitchEmitter.cpp:386:5
    #4 0x5605928c810f in js::frontend::BytecodeEmitter::emitSwitch(js::frontend::SwitchStatement*) /firefox/obj-js-debug/js/src/frontend/./../../../../js/src/frontend/BytecodeEmitter.cpp:2131:11
SUMMARY: UndefinedBehaviorSanitizer: SEGV /firefox/obj-js-debug/dist/include/mozilla/Assertions.h:261:3 in MOZ_CrashSequence(void*, long)
Attached patch fix.patch — — Splinter Review
Attached file crash_stack.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
See Also: → 1827359
Flags: needinfo?(jdemooij)
Blocks: sm-security
Severity: -- → S3
Priority: -- → P3
Duplicate of this bug: 2077726
Assignee: nobody → iireland
Status: UNCONFIRMED → ASSIGNED
Ever confirmed: true

:iain, please review the release status flags for beta, release, and ESR and update each one as appropriate.

For each version, indicate whether it is affected, unaffected, disabled, or wontfix if it is affected but does not need to be fixed on that branch.

Flags: needinfo?(iireland)

As mentioned in this comment on the dupe, I'm pretty sure this doesn't affect non-release builds at all. Opening up.

Group: javascript-core-security
Flags: needinfo?(jdemooij)
Flags: needinfo?(iireland)
Keywords: sec-other
Attachment #9654812 - Attachment description: (secure) → Bug 2076003: Force resume index to 0 for empty switches r=#spidermonkey-reviewers
Status: ASSIGNED → RESOLVED
Closed: 1 month ago
Resolution: --- → FIXED
Target Milestone: --- → 159 Branch
QA Whiteboard: [qa-triage-done-c160/b159]
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: