Closed Bug 1503071 Opened 2 years ago Closed 2 years ago

Assertion failure: s_.payload_.why_ == why, at dist/include/js/Value.h:699 with getBacktrace


(Core :: JavaScript Engine, defect, P1)




Tracking Status
firefox-esr60 --- wontfix
firefox63 --- wontfix
firefox64 --- wontfix
firefox65 --- fixed


(Reporter: decoder, Assigned: iain)


(4 keywords, Whiteboard: [jsbugmon:update])


(1 file)

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

setInterruptCallback(function() {
function foo(testBitNot = () => eval("a")) {
    var stack = getBacktrace({
        args: true


received signal SIGSEGV, Segmentation fault.
#0  0x0000555555a78990 in JS::Value::isMagic (why=JS_OPTIMIZED_OUT, this=<optimized out>) at dist/include/js/Value.h:699
#1  js::WrappedPtrOperations<JS::Value, JS::Handle<JS::Value> >::isMagic (why=JS_OPTIMIZED_OUT, this=<synthetic pointer>) at dist/include/js/Value.h:1313
#2  FormatValue (cx=<optimized out>, v=v@entry=..., bytes=...) at js/src/jsfriendapi.cpp:843
#3  0x0000555555a9cfdc in FormatFrame (showThisProps=false, showLocals=false, showArgs=true, num=2, sp=..., iter=..., cx=<optimized out>) at js/src/jsfriendapi.cpp:946
#4  JS::FormatStackDump (cx=<optimized out>, showArgs=true, showLocals=false, showThisProps=false) at js/src/jsfriendapi.cpp:1136
#5  0x0000555555ba57db in GetBacktrace (cx=<optimized out>, argc=<optimized out>, vp=<optimized out>) at js/src/builtin/TestingFunctions.cpp:3530
#6  0x000055555596d0e5 in CallJSNative (cx=0x7ffff5f18000, native=0x555555ba5770 <GetBacktrace(JSContext*, unsigned int, JS::Value*)>, args=...) at js/src/vm/Interpreter.cpp:468
#12 0x0000000000000000 in ?? ()
rax	0x0	0
rbx	0x7fffffffa1c0	140737488331200
rcx	0x7ffff6c1c2dd	140737333281501
rdx	0x0	0
rsi	0x7ffff6eeb770	140737336227696
rdi	0x7ffff6eea540	140737336223040
rbp	0x7fffffff9fb0	140737488330672
rsp	0x7fffffff9f50	140737488330576
r8	0x7ffff6eeb770	140737336227696
r9	0x7ffff7fe6cc0	140737354034368
r10	0x58	88
r11	0x7ffff6b927a0	140737332717472
r12	0x7fffffffa340	140737488331584
r13	0x7ffff4e00f90	140737301712784
r14	0x7fffffffa1b0	140737488331184
r15	0x7fffffffa1d0	140737488331216
rip	0x555555a78990 <FormatValue(JSContext*, JS::HandleValue, JS::UniqueChars&)+512>
=> 0x555555a78990 <FormatValue(JSContext*, JS::HandleValue, JS::UniqueChars&)+512>:	movl   $0x0,0x0
   0x555555a7899b <FormatValue(JSContext*, JS::HandleValue, JS::UniqueChars&)+523>:	ud2
Steven, can you triage this?
Flags: needinfo?(sdetar)
Nicolas, could you triage this bug?
Flags: needinfo?(sdetar) → needinfo?(nicolas.b.pierron)
This sounds similar to Bug 1423937.
Iain, can you investigate this issue?
Flags: needinfo?(nicolas.b.pierron) → needinfo?(iireland)
Priority: -- → P1
I'll take a look.
Flags: needinfo?(iireland)
Figured out the issue. Not sure about the fix yet.

Here's an updated testcase (run with --baseline-eager):

var g = true
setInterruptCallback(function() {
function foo(bt, x=3) {
    if (bt) {
	print(getBacktrace({args: true}));
    if (g) {
	g = false
    (function()  { n = bt;});

Like bug 1423937, this bug occurs while we are trying to print a stack frame that has been interrupted before it has had a chance to fully initialize its call object. Unlike that bug, this bug happens in baseline.

The key here is that foo has an argument with a default value. (If |x=3| is removed from the testcase above, it passes.) When constructing the call object, we copy aliased arguments into the call object manually, *except* for the case where the function has default parameters: (

    if (!frame.script()->bodyScope()->as<FunctionScope>().hasParameterExprs()) {
        // If there are no defaults, copy the aliased arguments into the call
        // object manually. If there are defaults, bytecode is generated to do
        // the copying.

The chain of events is as follows:
1. We call foo, which requests an interrupt and then calls itself recursively.
2. We create a frame for the recursive call.
3. We create a call object. Because foo has a parameter with a default value, we do not copy values into the call object. Instead, we wait for bytecode to do so.
4. Before we reach that bytecode, we check for interrupts. An interrupt triggers.
5. The interrupt tries to print a backtrace. When it gets to the interrupted frame, the call obj exists, but contains JS_UNINITIALIZED_LEXICAL instead of an actual value. FormatValue doesn't know how to print that.

Here's a backtrace:

0 foo(bt = true) ["/home/iain/src/interruptbt.js":9:7]
1 anonymous() ["/home/iain/src/interruptbt.js":4:4]
2 foo(bt = false) ["/home/iain/src/interruptbt.js":8:4] *** THIS IS THE FRAME WITHOUT A VALID CALL OBJ ***
3 foo(bt = false) ["/home/iain/src/interruptbt.js":14:1]
4 <TOP LEVEL> ["/home/iain/src/interruptbt.js":18:0]

The easiest fix is to check the return value from CallObject::aliasedBinding in FormatFrame, and handle the magic value specially. That might just paper over other situations where the same thing can happen, though.
The testcase in comment 5 seems to go back to before the following changeset from Jan 2015:
Whiteboard: [jsbugmon:update,bisect] → [jsbugmon:update]
Don't worry about trying to bisect anything on this bug, Gary. It's a corner case that we never handled, not a regression.
Assignee: nobody → iireland
Pushed by
Handle JS_UNINITIALIZED_LEXICAL in FormatValue r=tcampbell
Closed: 2 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla65
You need to log in before you can comment on or make changes to this bug.