Closed
Bug 1353763
Opened 8 years ago
Closed 8 years ago
Crash [@ js::gc::IsInsideNursery]
Categories
(Core :: JavaScript Engine, defect)
Tracking
()
VERIFIED
FIXED
mozilla55
Tracking | Status | |
---|---|---|
firefox-esr45 | --- | unaffected |
firefox52 | --- | unaffected |
firefox-esr52 | --- | unaffected |
firefox53 | --- | wontfix |
firefox54 | --- | wontfix |
firefox55 | --- | verified |
People
(Reporter: gkw, Assigned: luke)
References
Details
(4 keywords, Whiteboard: [jsbugmon:update][adv-main55+])
Crash Data
Attachments
(3 files, 1 obsolete file)
(deleted),
text/plain
|
Details | |
(deleted),
patch
|
bbouvier
:
review+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
bbouvier
:
review+
|
Details | Diff | Splinter Review |
The following testcase crashes on mozilla-central revision b043233ec04f (build with --enable-debug --enable-more-deterministic, run with --fuzzing-safe --no-threads --ion-eager):
// Adapted from randomly chosen test: js/src/jit-test/tests/debug/execution-observability-03.js
p = new Proxy(this, {
"deleteProperty": function () {
g = newGlobal();
g.parent = this;
g.eval("Debugger(parent).onEnterFrame = function() {};");
return 1;
}
});
delete p.f;
// Adapted from randomly chosen test: js/src/jit-test/tests/wasm/timeout/1.js
timeout(0.01);
var code = wasmTextToBinary('(module(func(export "f1")(loop$top br$top)))');
new WebAssembly.Instance(new WebAssembly.Module(code)).exports.f1();
Backtrace:
#0 js::gc::IsInsideNursery (cell=0xe4e4e4e400000000) at /home/ubuntu/shell-cache/js-dbg-64-dm-linux-b043233ec04f/objdir-js/dist/include/js/HeapAPI.h:328
#1 js::gc::Cell::isTenured (this=0xe4e4e4e400000000) at js/src/gc/Heap.h:251
#2 JSObject::readBarrier (obj=0xe4e4e4e400000000) at js/src/jsobj.h:644
#3 js::InternalBarrierMethods<js::WasmInstanceObject*>::readBarrier (v=0xe4e4e4e400000000) at js/src/gc/Barrier.h:266
#4 js::ReadBarrieredBase<js::WasmInstanceObject*>::read (this=0x7f0329759068) at js/src/gc/Barrier.h:553
/snip
For detailed crash information, see attachment.
Setting s-s because GC is on the stack, as a start.
Reporter | ||
Comment 1•8 years ago
|
||
Reporter | ||
Comment 2•8 years ago
|
||
autoBisect shows this is probably related to the following changeset:
The first bad revision is:
changeset: https://hg.mozilla.org/mozilla-central/rev/766ead465209
user: Yury Delendik
date: Sat Jan 07 10:36:11 2017 -0600
summary: Bug 1286948 - Extends AbstractFramePtr to reference wasm::DebugFrame. r=luke,shu
Yury, is bug 1286948 a likely regressor?
Blocks: 1286948
Flags: needinfo?(ydelendik)
Updated•8 years ago
|
status-firefox52:
--- → unaffected
status-firefox53:
--- → affected
status-firefox54:
--- → affected
status-firefox-esr45:
--- → unaffected
status-firefox-esr52:
--- → unaffected
Comment 3•8 years ago
|
||
what does the 0xe4 memory poison value mean?
calling this "sec-moderate" because it looks like Debugger calls not available to the web are involved, but if the underlying bug can be hit from a normal context we should raise this to sec-high.
Keywords: sec-moderate
Comment 4•8 years ago
|
||
(In reply to Daniel Veditz [:dveditz] from comment #3)
> what does the 0xe4 memory poison value mean?
It's our junk fill [1]. Basically what you get when you malloc something but don't initialize it.
[1] http://searchfox.org/mozilla-central/rev/b8cce081200129a1307e2a682f121135b5ca1d19/memory/mozjemalloc/jemalloc.c#5107-5112
Assignee | ||
Comment 5•8 years ago
|
||
Ah hah, what's happening here is that the timeout() is asynchronously interrupting execution while wasm frames are on the stack. Because the async interrupt doesn't set WasmActivation::exitFP, FrameIter doesn't find any frames and so the soon-to-be-popped frames are left in the debugger's frame map where they later explode.
I think the fix is for the interrupt callback to pass 'fp' to WasmHandleExecutionInterrupt() so that it can set exitFP. This would actually strengthen our invariants about WasmActivation::exitFP and remove some async-interrupt special cases in other places.
Assignee | ||
Comment 6•8 years ago
|
||
I'm pretty happy about this patch: it removes an entire case of "interrupted and lost the stack" with a pretty aggressive assert in FrameIterator. It also means we always get full stacks in slow-script-interrupt situations.
Assignee: nobody → luke
Attachment #8857584 -
Flags: review?(bbouvier)
Assignee | ||
Comment 7•8 years ago
|
||
Unrelated, I saw this test timeout on non-JIT and eager runs. Disabling in these cases allows the test to run in 7s without loss of coverage.
Attachment #8857585 -
Flags: review?(bbouvier)
Assignee | ||
Comment 8•8 years ago
|
||
Actually, the new tighter assertion shows that we now depend on the builtin thunks (b/c some of them can GC, and thus iterate the stack). Great timing on that patch Benjamin!
Blocks: 1340219
Assignee | ||
Comment 9•8 years ago
|
||
Testing on windows revealed that GetThreadContext() on Win64 was never returning fp; the CONTEXT_CONTROL flag had to be upgraded to CONTEXT_FULL to get fp (but only on Win64; on Win32, CONTEXT_CONtroL gives you fp...). Yay for stronger assertions!
Attachment #8857584 -
Attachment is obsolete: true
Attachment #8857584 -
Flags: review?(bbouvier)
Attachment #8857666 -
Flags: review?(bbouvier)
Comment 10•8 years ago
|
||
Comment on attachment 8857585 [details] [diff] [review]
make-test-faster
Review of attachment 8857585 [details] [diff] [review]:
-----------------------------------------------------------------
Ha, fun!
Attachment #8857585 -
Flags: review?(bbouvier) → review+
Comment 11•8 years ago
|
||
Comment on attachment 8857666 [details] [diff] [review]
fix-interrupt-fp
Review of attachment 8857666 [details] [diff] [review]:
-----------------------------------------------------------------
So nice.
That makes me wonder about the remaining builtin calls that don't get thunked, but I think that for these we're good as well, because either they have their own exit prologue/epilogue (e.g. callimport, coerce_in_place, traps and debug trap, ), or they'll inherit the FP value of the caller (e.g. throw, OOB, unaligned accesses, and now handle interrupt with this patch). Good.
::: js/src/jit/arm/Simulator-arm.cpp
@@ +1555,5 @@
> +void
> +Simulator::handleWasmInterrupt()
> +{
> + void* pc = reinterpret_cast<void*>(get_pc());
> + uint8_t* fp = reinterpret_cast<uint8_t*>(get_register(r11));
s/r11/FramePointer/ ?
@@ +1580,5 @@
> if (!act)
> return false;
>
> void* pc = reinterpret_cast<void*>(get_pc());
> + uint8_t* fp = reinterpret_cast<uint8_t*>(get_register(r11));
(same comment as above)
::: js/src/vm/Stack.cpp
@@ +1687,5 @@
> + // interrupt-during-interrupt.
> + MOZ_ASSERT(!interrupted());
> + MOZ_ASSERT(compartment()->wasm.lookupCode(pc)->lookupRange(pc)->isFunction());
> + MOZ_ASSERT(pc);
> + MOZ_ASSERT(fp);
I'd move the last two assertions before the lookupCode assertion (since the lookupCode assertion will fail if !pc, but having the pc assertion trigger first is more explanatory).
@@ +1697,5 @@
> +void
> +WasmActivation::finishInterrupt()
> +{
> + MOZ_ASSERT(resumePC_);
> + MOZ_ASSERT(exitFP_);
Or just MOZ_ASSERT(interrupted())?
::: js/src/vm/Stack.h
@@ +1764,5 @@
> + // simulator) and cleared by WasmHandleExecutionInterrupt or WasmHandleThrow
> + // when the interrupt is handled.
> + void startInterrupt(void* pc, uint8_t* fp);
> + void finishInterrupt();
> + bool interrupted() const { return !!resumePC_; }
Would it make sense that in the dtor of WasmActivation, we'd assert !interrupted()? (although the interrupt stub handler doesn't reset the state, so i'm not sure it holds as is)
::: js/src/wasm/WasmFrameIterator.cpp
@@ -235,4 @@
> // Only non-imported functions can have debug frames.
> return code_->metadata().debugEnabled &&
> - fp_ &&
> - !missingFrameMessage_ &&
I like these simplifications!
::: js/src/wasm/WasmSignalHandlers.cpp
@@ +843,1 @@
> // to InterruptRunningCode's use of SuspendThread. When this happens,
pre-existing: InterruptRunningCode has changed name. There's a second occurrence above.
Attachment #8857666 -
Flags: review?(bbouvier) → review+
Updated•8 years ago
|
Flags: needinfo?(ydelendik)
Assignee | ||
Comment 12•8 years ago
|
||
(In reply to Benjamin Bouvier [:bbouvier] from comment #11)
Thanks!
> s/r11/FramePointer/ ?
Unfortunately, the ARM simulator has its own set of enums for registers so FramePointer isn't compatible.
> Would it make sense that in the dtor of WasmActivation, we'd assert
> !interrupted()? (although the interrupt stub handler doesn't reset the
> state, so i'm not sure it holds as is)
Technically we already do (by asserting resumePC_ == null). The interrupt state is always cleared on both the continue and stop paths (by WasmHandleExecutionInterrupt and WasmHandleThrow, resp.). I can change to !interrupted(), though.
Assignee | ||
Comment 13•8 years ago
|
||
Landed:
https://hg.mozilla.org/integration/mozilla-inbound/rev/f90707333e7f6daaaf364bc61890ba2b1849199d
https://hg.mozilla.org/integration/mozilla-inbound/rev/20e85b5bd3429c3e7386b56fe6c6056d52219eb0
This created an intermittent win8 failure of asm.js/testBug1117255.js that I had missed in try and local runs. Reducing the shell's watchdog timer interval from .1s to 1ms makes it reproduce easily, though. With printf logging I can see a most bizarre sequence:
1. the watchdog interrupts the thread at PC x and attempts to redirect to PC y
2. an OOB fault at PC x fires (probably it had already fired internally before step 1, but hadn't bubbled up to userspace yet)
3. the OOB handler handles successfully by redirecting PC and this *clobbers* the redirect in made in step 1
4. now WasmActivation::interrupt() is left 'true' even though the interrupt handler will never be called (b/c of step 3)
Fortunately, just like the other weird case in the Windows handler, this is precisely detectable and has a simple fix (once you understand the madness):
https://hg.mozilla.org/integration/mozilla-inbound/rev/b411de2683e99d545e82f55113a0f0dc2920808b
Comment 14•8 years ago
|
||
https://hg.mozilla.org/mozilla-central/rev/20e85b5bd342
https://hg.mozilla.org/mozilla-central/rev/f90707333e7f
https://hg.mozilla.org/mozilla-central/rev/b411de2683e9
Status: NEW → RESOLVED
Closed: 8 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla55
Assignee | ||
Comment 16•8 years ago
|
||
IIUC, the bug can only be activated by the debugger combined with the slow-script dialog. I'm not sure what the policy is for rare devtools-only crashes, but I'd guess this didn't require uplift and letting the fix ride the trains would be fine.
Flags: needinfo?(luke)
Comment 17•8 years ago
|
||
Good enough for me.
Updated•8 years ago
|
Group: javascript-core-security → core-security-release
Updated•8 years ago
|
Status: RESOLVED → VERIFIED
Comment 18•8 years ago
|
||
JSBugMon: This bug has been automatically verified fixed.
Updated•7 years ago
|
Whiteboard: [jsbugmon:update] → [jsbugmon:update][adv-main55+]
Updated•7 years ago
|
Group: core-security-release
You need to log in
before you can comment on or make changes to this bug.
Description
•