Closed Bug 1878261 Opened 8 months ago Closed 8 months ago

Crash [@ JS::Compartment::wrap] with Debugger and interrupt callback

Categories

(Core :: JavaScript Engine, defect, P1)

x86_64
Linux
defect

Tracking

()

VERIFIED FIXED
124 Branch
Tracking Status
firefox-esr115 --- unaffected
firefox122 --- unaffected
firefox123 --- unaffected
firefox124 --- verified

People

(Reporter: decoder, Assigned: anba)

References

(Regression)

Details

(4 keywords, Whiteboard: [bugmon:update,bisected,confirmed])

Crash Data

Attachments

(4 files)

The following testcase crashes on mozilla-central revision 20240202-854c462a083b (debug build, run with --fuzzing-safe --ion-offthread-compile=off --baseline-eager test.js):

gczeal(7, 1)
function a(b) {
    c = newGlobal({
        newCompartment: true
    })
    d = new Debugger
    setInterruptCallback(function() {
        d.addDebuggee(c)
        d.getNewestFrame().onStep = function() {
            return b
        }
        return true
    })
    try {
        c.eval("(" + function() {
            invokeInterruptCallback(function() {})
        } + ")()")
    } finally {}
}
a({
    throw: "thrown 42"
})

Backtrace:

received signal SIGSEGV, Segmentation fault.
#0  0x0000555b7e2033db in JS::Compartment::wrap(JSContext*, JS::MutableHandle<JSString*>) ()
#1  0x0000555b7deb61e4 in JS::Compartment::wrap(JSContext*, JS::MutableHandle<JS::Value>) ()
#2  0x0000555b7e2c98ac in JSContext::getPendingException(JS::MutableHandle<JS::Value>) ()
#3  0x0000555b7e2644ed in JS::GetPendingExceptionStack(JSContext*, JS::ExceptionStack*) ()
#4  0x0000555b7e2643df in JS::StealPendingExceptionStack(JSContext*, JS::ExceptionStack*) ()
#5  0x0000555b7de89cd1 in js::shell::AutoReportException::~AutoReportException() ()
#6  0x0000555b7de92c01 in Shell(JSContext*, js::cli::OptionParser*) ()
#7  0x0000555b7de8c459 in main ()
rax	0x6c6a4000001	7450224754689
rbx	0x7f5f9eb44000	140048661233664
rcx	0xfffe2f2f2f2f2f2c	-511070251831508
rdx	0x7fff0a6206e0	140733367584480
rsi	0x7f5f9fd2e100	140048680018176
rdi	0x7f5f9fd03240	140048679842368
rbp	0x7fff0a6206b0	140733367584432
rsp	0x7fff0a620670	140733367584368
r8	0x0	0
r9	0x7fff0a61e301	140733367575297
r10	0x7fff0a7e5080	140733369438336
r11	0x202	514
r12	0x7f5f9fd03240	140048679842368
r13	0x6c6a4000508	7450224755976
r14	0x7fff0a6206e0	140733367584480
r15	0x7f5f9fd2e100	140048680018176
rip	0x555b7e2033db <JS::Compartment::wrap(JSContext*, JS::MutableHandle<JSString*>)+107>
=> 0x555b7e2033db <_ZN2JS11Compartment4wrapEP9JSContextNS_13MutableHandleIP8JSStringEE+107>:	cmp    %rbx,(%rcx)
   0x555b7e2033de <_ZN2JS11Compartment4wrapEP9JSContextNS_13MutableHandleIP8JSStringEE+110>:	jne    0x555b7e20340f <_ZN2JS11Compartment4wrapEP9JSContextNS_13MutableHandleIP8JSStringEE+159>

This involves the debugger so I assume it is sec-moderate at most, however with the poison pattern in the crash, we should confirm that first.

Attached file Testcase

Verified bug as reproducible on mozilla-central 20240202094312-dd4c0135beb5.
The bug appears to have been introduced in the following build range:

Start: 82dfbdd770bc54674f82bae256dae683772884af (20240122155520)
End: 75c3c3ed6fe2c33aa435e3a099c5f18be4b4d8d2 (20240122183000)
Pushlog: https://hg.mozilla.org/integration/autoland/pushloghtml?fromchange=82dfbdd770bc54674f82bae256dae683772884af&tochange=75c3c3ed6fe2c33aa435e3a099c5f18be4b4d8d2

Whiteboard: [bugmon:update,bisect] → [bugmon:update,bisected,confirmed]

I believe the problem is in this code. We store the exception, which is a nursery string, in rfe->exception. Then we store the exception stack. It's from another compartment, so we have to wrap it. Allocating the wrapper triggers a minor GC, which promotes the exception string. However, despite our use of MutableHandleValue::fromMarkedLocation to make a handle out of it, rfe isn't actually traced. The exception value stored in rfe now points to freed nursery space.

I don't immediately see a reason that this couldn't be done without the debugger. It only requires a cross-compartment exception stack, and a very precisely timed GC. I'm going to conservatively bump this up to sec-high.

This is a regression from bug 1843499, although the first dubious use of fromMarkedLocation here appears to date back all the way to bug 951282.

Keywords: sec-moderatesec-high
Regressed by: 1843499
Severity: -- → S2
Priority: -- → P1
Flags: needinfo?(andrebargull)
Assignee: nobody → andrebargull
Status: NEW → ASSIGNED

Set release status flags based on info from the regressing bug 1843499

(In reply to Iain Ireland [:iain] from comment #4)

I believe the problem is in this code.

Yes, that sounds plausible. Explicitly rooting the variables fixes the crash.

Flags: needinfo?(andrebargull)

Here's a testcase that triggers the bug without needing the debugger:

// |jit-test| --baseline-eager

var g = newGlobal({newCompartment: true});

function foo() {
  try {
    g.eval("gczeal(7,1); throw 'a thrown string'");
  } finally {
    gczeal(0);
  }
}

try {
  foo();
} catch (e) { assertEq(e, 'a thrown string')}

We can land it separately.

Pushed by andre.bargull@gmail.com: https://hg.mozilla.org/integration/autoland/rev/df0b9539f560 Prefer local variables over fromMarkedLocation. r=iain
Group: javascript-core-security → core-security-release
Status: ASSIGNED → RESOLVED
Closed: 8 months ago
Flags: in-testsuite?
Resolution: --- → FIXED
Target Milestone: --- → 124 Branch

Verified bug as fixed on rev mozilla-central 20240207041740-79b383834481.
Removing bugmon keyword as no further action possible. Please review the bug and re-add the keyword for further analysis.

Status: RESOLVED → VERIFIED
Keywords: bugmon
Group: core-security-release
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: