Closed Bug 1572391 Opened 4 months ago Closed Last month

Assertion failure: frame.isDebuggee(), at js/src/debugger/Debugger.cpp:3186

Categories

(Core :: JavaScript Engine, defect, P1, critical)

x86_64
Linux
defect

Tracking

()

RESOLVED FIXED
mozilla71
Tracking Status
firefox-esr60 --- unaffected
firefox-esr68 --- unaffected
firefox67 --- unaffected
firefox68 --- unaffected
firefox69 --- wontfix
firefox70 --- wontfix
firefox71 --- fixed

People

(Reporter: decoder, Assigned: jimb)

References

(Blocks 2 open bugs, Regression)

Details

(4 keywords, Whiteboard: [jsbugmon:update][debugger-mvp])

Attachments

(1 file)

The following testcase crashes on mozilla-central revision b94a6b06c9b9 (build with --enable-valgrind --enable-gczeal --disable-tests --disable-profiling --enable-debug --enable-optimize, run with --fuzzing-safe --ion-offthread-compile=off):

g = newGlobal({ newCompartment: true });
g.parent = this
g.eval("(" + function() {
    Debugger(parent).onExceptionUnwind = function(frame) {
        frame.older
    }
} + ")()")
function* countdown(n38) {
    while (n38 > 0) {
        yield* countdown(--n38);
    }
    return apply != 3;
}
function collect_results(iter) {
    result = iter.next();
}
collect_results(countdown(3));

Backtrace:

received signal SIGSEGV, Segmentation fault.
#0  js::Debugger::ensureExecutionObservabilityOfFrame (cx=0x7ffff5f23000, frame=...) at js/src/debugger/Debugger.cpp:3185
#1  0x0000555555e2b84a in js::Debugger::getFrame (this=this@entry=0x7ffff5f71000, cx=<optimized out>, cx@entry=0x7ffff5f23000, iter=..., result=..., result@entry=...) at js/src/debugger/Debugger.cpp:592
#2  0x0000555555e2bdf5 in js::DebuggerFrame::getOlder (cx=0x7ffff5f23000, frame=..., frame@entry=..., result=..., result@entry=...) at js/src/debugger/Frame.cpp:516
#3  0x0000555555e2bf5a in js::DebuggerFrame::olderGetter (cx=<optimized out>, cx@entry=0x7ffff5f23000, argc=<optimized out>, vp=<optimized out>) at js/src/debugger/Frame.cpp:1217
#4  0x00005555558e789f in CallJSNative (cx=0x7ffff5f23000, native=native@entry=0x555555e2beb0 <js::DebuggerFrame::olderGetter(JSContext*, unsigned int, JS::Value*)>, args=...) at js/src/vm/Interpreter.cpp:448
#5  0x00005555558de00a in js::InternalCallOrConstruct (cx=<optimized out>, cx@entry=0x7ffff5f23000, args=..., construct=construct@entry=js::NO_CONSTRUCT) at js/src/vm/Interpreter.cpp:540
#6  0x00005555558de76f in InternalCall (cx=cx@entry=0x7ffff5f23000, args=...) at js/src/vm/Interpreter.cpp:595
#7  0x00005555558de8f0 in js::Call (cx=cx@entry=0x7ffff5f23000, fval=..., fval@entry=..., thisv=..., thisv@entry=..., args=..., rval=...) at js/src/vm/Interpreter.cpp:611
#8  0x00005555558deaa8 in js::CallGetter (cx=0x7ffff5f23000, thisv=..., thisv@entry=..., getter=..., getter@entry=..., rval=..., rval@entry=...) at js/src/vm/Interpreter.cpp:735
#9  0x0000555555b17120 in CallGetter (vp=..., shape=..., receiver=..., obj=..., cx=<optimized out>) at js/src/vm/NativeObject.cpp:2269
#10 GetExistingProperty<(js::AllowGC)1> (cx=0x7ffff5f23000, receiver=..., obj=..., shape=..., vp=...) at js/src/vm/NativeObject.cpp:2324
#11 0x0000555555b19d04 in NativeGetPropertyInline<(js::AllowGC)1> (cx=<optimized out>, cx@entry=0x7ffff5f23000, obj=..., receiver=..., id=..., nameLookup=nameLookup@entry=NotNameLookup, vp=...) at js/src/vm/NativeObject.cpp:2570
#12 0x0000555555b1a310 in js::NativeGetProperty (cx=cx@entry=0x7ffff5f23000, obj=..., receiver=..., receiver@entry=..., id=..., id@entry=..., vp=..., vp@entry=...) at js/src/vm/NativeObject.cpp:2608
#13 0x00005555558eb3a4 in js::GetProperty (cx=0x7ffff5f23000, obj=..., receiver=..., id=..., vp=...) at js/src/vm/ObjectOperations-inl.h:117
#14 0x00005555558c9920 in js::GetProperty (vp=..., name=<optimized out>, receiver=..., obj=..., cx=<optimized out>) at js/src/vm/ObjectOperations-inl.h:124
#15 js::GetProperty (cx=<optimized out>, v=..., name=..., vp=...) at js/src/vm/Interpreter.cpp:4513
#16 0x00005555558cfabb in GetPropertyOperation (vp=..., lval=..., pc=<optimized out>, script=..., fp=<optimized out>, cx=0x7ffff5f23000) at js/src/vm/Interpreter.cpp:218
#17 Interpret (cx=0x7ffff5f23000, state=...) at js/src/vm/Interpreter.cpp:2771
#18 0x00005555558dd9f6 in js::RunScript (cx=0x7ffff5f23000, state=...) at js/src/vm/Interpreter.cpp:425
#19 0x00005555558de32f in js::InternalCallOrConstruct (cx=<optimized out>, cx@entry=0x7ffff5f23000, args=..., construct=construct@entry=js::NO_CONSTRUCT) at js/src/vm/Interpreter.cpp:568
#20 0x00005555558de76f in InternalCall (cx=0x7ffff5f23000, args=...) at js/src/vm/Interpreter.cpp:595
#21 0x00005555558de8f0 in js::Call (cx=<optimized out>, fval=..., fval@entry=..., thisv=..., thisv@entry=..., args=..., rval=...) at js/src/vm/Interpreter.cpp:611
#22 0x0000555555e366bc in js::Call (rval=..., arg1=..., arg0=..., thisObj=<optimized out>, fval=..., cx=<optimized out>) at js/src/vm/Interpreter.h:123
#23 js::Debugger::fireExceptionUnwind (this=this@entry=0x7ffff5f71000, cx=<optimized out>, vp=..., vp@entry=...) at js/src/debugger/Debugger.cpp:2086
#24 0x0000555555e3c335 in js::DebugAPI::<lambda(js::Debugger*)>::operator() (dbg=0x7ffff5f71000, __closure=<synthetic pointer>) at js/src/debugger/Debugger.cpp:1073
#25 js::Debugger::dispatchHook<js::DebugAPI::slowPathOnExceptionUnwind...) at js/src/debugger/Debugger.cpp:2214
#26 js::DebugAPI::slowPathOnExceptionUnwind (cx=<optimized out>, frame=...) at js/src/debugger/Debugger.cpp:1074
#27 0x00005555558cf1e8 in js::DebugAPI::onExceptionUnwind (frame=..., frame@entry=..., cx=<optimized out>) at js/src/debugger/DebugAPI-inl.h:151
#28 HandleError (regs=..., cx=<optimized out>) at js/src/vm/Interpreter.cpp:1245
#29 Interpret (cx=0x7ffff5f23000, state=...) at js/src/vm/Interpreter.cpp:4392
[...]
#39 main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at js/src/shell/js.cpp:11375
rax	0x555557cfb200	93825033810432
rbx	0x7fffffffab08	140737488333576
rcx	0x555556c3fae0	93825016265440
rdx	0x0	0
rsi	0x7ffff6eeb770	140737336227696
rdi	0x7ffff6eea540	140737336223040
rbp	0x7fffffffab60	140737488333664
rsp	0x7fffffffab00	140737488333568
r8	0x7ffff6eeb770	140737336227696
r9	0x7ffff7fe6cc0	140737354034368
r10	0x58	88
r11	0x7ffff6b927a0	140737332717472
r12	0x7ffff5f23000	140737319677952
r13	0x7ffff5f71158	140737319997784
r14	0x7fffffffac60	140737488333920
r15	0x7fffffffad40	140737488334144
rip	0x555555e066e9 <js::Debugger::ensureExecutionObservabilityOfFrame(JSContext*, js::AbstractFramePtr)+297>
=> 0x555555e066e9 <js::Debugger::ensureExecutionObservabilityOfFrame(JSContext*, js::AbstractFramePtr)+297>:	movl   $0x0,0x0
   0x555555e066f4 <js::Debugger::ensureExecutionObservabilityOfFrame(JSContext*, js::AbstractFramePtr)+308>:	ud2

autobisectjs shows this is probably related to the following changeset:

The first bad revision is:
changeset: https://hg.mozilla.org/mozilla-central/rev/8a7c8f8ecd56
user: Jim Blandy
date: Mon Jun 10 20:06:34 2019 +0000
summary: Bug 1539654: Ensure generator scripts observed by Debugger.Frames are marked as debuggees. r=jorendorff

Jim, is bug 1539654 a likely regressor?

Flags: needinfo?(jimb)
Regressed by: 1539654
Whiteboard: [jsbugmon:update,bisect] → [jsbugmon:update]

Emailing followup.

Assignee: nobody → jimb

I'm looking into this.

Flags: needinfo?(jimb)

The bug is that js::DebuggerFrame::setGenerator increments the frame's script's generator observer count, making the script a debuggee, without marking all frames running that script debuggees as well. This violates the invariant that any frame running a debuggee script must itself be a debuggee.

The ensureExecutionObservability... functions are designed to cope with this sort of thing; I think setGenerator may need to be using one of those, rather than calling DebugScript::incrementGeneratorObserverCount directly.

Priority: -- → P1
Blocks: dbg-71
Whiteboard: [jsbugmon:update] → [jsbugmon:update][debugger-mvp]
Status: NEW → ASSIGNED

A Debugger.Frame referring to a generator or async call must track all
resumptions of that call. For the debugger to detect resumptions, the
generator/async function's script must be compiled with instrumentation on
JSOP_AFTERYIELD bytecodes. DebuggerFrame::setGenerator calls DebugScript::
incrementGeneratorObserverCount to request this.

However, incrementing the generator observer count marks the script as a
debuggee script. It is an invariant of the debugger that all frames running
debuggee scripts must themselves be marked as debuggee frames. So
DebuggerFrame::setGenerator needs to also call
Debugger::ensureExecutionObservabilityOfScript, which walks the stack and marks
other frames as debuggees where necessary.

Any updates on this.

Flags: needinfo?(jimb)

Waiting on review from jorendorff. I'll ping him.

Flags: needinfo?(jimb)

Jason if you can review this patch it can at least make it into 71 nightly (if not 70)

Flags: needinfo?(jorendorff)
Flags: needinfo?(jorendorff)

Reviewer changed to Ted Campbell.

Pushed by jblandy@mozilla.com:
https://hg.mozilla.org/integration/autoland/rev/d46ee3823a5a
Mark all frames running a observed generator script as debuggees. r=tcampbell
Status: ASSIGNED → RESOLVED
Closed: Last month
Resolution: --- → FIXED
Target Milestone: --- → mozilla71
You need to log in before you can comment on or make changes to this bug.