Closed
Bug 636879
Opened 14 years ago
Closed 14 years ago
Assertion failure: (uint8*)ic.funGuard.executableAddress() + ic.joinPointOffset == returnAddress
Categories
(Core :: JavaScript Engine, defect)
Tracking
()
RESOLVED
FIXED
Tracking | Status | |
---|---|---|
status1.9.2 | --- | unaffected |
status1.9.1 | --- | unaffected |
People
(Reporter: decoder, Assigned: dvander)
References
Details
(Keywords: assertion, testcase, Whiteboard: [fixed-in-tracemonkey])
Attachments
(2 files)
865 bytes,
application/x-compressed-tar
|
Details | |
2.15 KB,
patch
|
billm
:
review+
luke
:
review+
dmandelin
:
approval2.0+
|
Details | Diff | Splinter Review |
The attached testcase (shell testcase, unpack archive, change into directory and run "js -j -m -a main.js") causes the following assertion on TM tip:
Assertion failure: (uint8*)ic.funGuard.executableAddress() + ic.joinPointOffset == returnAddress, at ./methodjit/MethodJIT.cpp:1074
Locking on recommendation of dvander.
Comment 1•14 years ago
|
||
Reduced to a single script:
var m = {A: "Object.defineProperty(Array.prototype, 'q', " +
"{enumerable: true, get: function() { x; }}); " +
"try { x; } catch(e) { } Array.prototype.r = null;",
B: '',
C: "options('tracejit');"};
var ieval = eval;
var arr = [["A"], ["B"], [], ["B", "B", "C"]];
for (var j = 0; j < arr.length; j++) {
var files = arr[j];
for (var i in files) {
try {
var desc = Object.getOwnPropertyDescriptor(m, files[i]);
ieval(desc.value);
} catch (e) {
}
}
}
Comment 2•14 years ago
|
||
I managed to squeeze out a little more of the craziness. Debugging.
HOTLOOP = 8;
var p = {q: ""};
var obj = Object.create(p);
for (var t = 0; t < HOTLOOP - 1; t++)
obj[t] = "";
obj[HOTLOOP - 1] = "options('tracejit');";
var ieval = eval;
for (var i in obj)
ieval(obj[i]);
print("OK"); // crashes after this executes
Comment 3•14 years ago
|
||
The tracejit starts recording the `for (var i in obj)` loop. The loop calls eval indirectly.
This re-enters the methodjit, which causes trace recording to stop. Then, in methodjit-compiled eval code, we call options(). This clears cx->traceJitEnabled. It does nothing else significant; we are not currently recording and no tracejit code is on the stack. If I change Options to just clear that one flag and return true, the bug still occurs. Comment out that line and the bug doesn't happen.
The methodjit returns to EvalKernel, which returns to the interpreter. At this point in js::Interpret, leaveOnSafePoint is false. Should it be true?
Comment 4•14 years ago
|
||
In case the question at the end of comment 3 does not point to the bug, here's what happens after that...
We continue in the interpreter: JSOP_POP, JSOP_MOREITER, JSOP_IFNE. CASE(JSOP_IFNE) contains a BRANCH(), which we execute. The BRANCH includes a call to MONITOR_BRANCH.
Here we test cx->traceJitEnabled. If it were set, we would call MonitorLoopEdge, which would effectively do nothing and return MONITOR_NOT_RECORDING, and everything would be fine. But it is cleared, so we MONITOR_BRANCH_METHODJIT() instead. This calls back into methodjit code (JaegerShotAtSafePoint). This seems to finish OK; once it returns back to js::Interpret, we jump to leave_on_safe_point and exit.
Then we're back in RunTracer. It sees that we have no extra frames and we're at a safe point, so it returns to the toplevel methodjit code, which crashes after a few instructions:
001C1081 call js::mjit::stubs::InvokeTracer (0F1380Ah)
001C1086 test eax,eax ;; eax is 001C04B6
001C1088 je 001C04B6
001C108E mov ebx,dword ptr [esp+1Ch]
001C1092 jmp eax
001C04B6 cmp dword ptr ds:[4602C0h],0
001C04BD jne 001C0E6F
001C04C3 mov edi,dword ptr [ebx+30h]
001C04C6 cmp dword ptr [edi+4],13F1888h ;; crashes, edi is 0
001C04CD jne 001C0E93
Comment 5•14 years ago
|
||
Is this a shell-only bug or could something like options() happen in web content?
/be
Comment 6•14 years ago
|
||
window._options is all I'm aware of, but it's gone. An extension could toggle in response to something in content, but this seems far-fetched. If you're thinking to unhide, makes sense to me.
Total strawman for the future: perhaps JIT options should be part of the script at compile time and not dynamically changeable?
Comment 7•14 years ago
|
||
The patch I put in bug 637385 helped me step through this.
Assignee | ||
Comment 8•14 years ago
|
||
001C04C3 mov edi,dword ptr [ebx+30h]
001C04C6 cmp dword ptr [edi+4],13F1888h ;; crashes, edi is 0
This appears to be a guard on local->clasp == js_IteratorClass. Smells like the stack being unbalanced somewhere. I'm trying to determine how this relates to the options-flip.
Assignee | ||
Comment 9•14 years ago
|
||
Thanks to Jason's explanation I think I can see what's going on. The call stack looks like:
> 0: <jit code>
> 1: JaegerShotAtSafePoint()
> 2: Interpret()
> 3: RunTracer()
When we return from frame 1, we instantly leave the interpreter through leaveAtSafePoiint, then return back to RunTracer. The current JSStackFrame, though, has actually been finished. So we start executing a frame at a totally wrong place.
I think the bug here is just that cx->regs->pc hasn't been updated, but also we must signal that ScriptEpilogue has been run by setting the finishedInInterpreter flag on the frame.
Bill, does that make sense?
Assignee | ||
Comment 10•14 years ago
|
||
Maybe we could kill two birds with one stone, let FrameIsFinished() peek for the finishedInInterpreter flag, and set that flag after leaveOnSafePoint.
This means case #5 of the big comment in HandleFinishedFrame is slightly mistaken: here we have a situation where the method JIT ran, but we weren't able to tell that it ran, so we treat it as an interpreter frame.
Assignee | ||
Comment 11•14 years ago
|
||
Fixes bug 636889 too. This is extremely hard to hit but it's a real bug, i.e. not related to options(). I can't say how security sensitive it is, both cases are only sg:dos but re-running completed code is pretty of scary. We should fix this for 4.
Comment on attachment 515808 [details] [diff] [review]
fix
I have to admit that I don't really understood this code very well. It might be good to check with Luke as well.
We might want to rename the finishedInInterpreter flag to something like finishedScriptEpilogue instead.
Attachment #515808 -
Flags: review?(wmccloskey) → review+
Assignee | ||
Updated•14 years ago
|
Attachment #515808 -
Flags: review?(lw)
Reporter | ||
Comment 14•14 years ago
|
||
(In reply to comment #11)
> Fixes bug 636889 too. This is extremely hard to hit but it's a real bug, i.e.
> not related to options().
Is it possible to derive a test case that does not use options()? I'd be curious to analyze the impact of this, but I didn't manage to remove it in the test case.
Maybe I can check how security sensitive it is then.
Assignee | ||
Comment 15•14 years ago
|
||
These things are pretty hard to test case; did bug 636889 flip options? You have to start recording a loop from the mjit, then abort, then fail to record that loop again and instead run it in the mjit.
Bill: Another way to think about this bug is that at MONITOR_BRANCH_METHODJIT(), we should have a LEAVE_AT_SAFEPOINT or something, because FinishExcessFrames() is supposed to be calling JaegerShotAtSafePoint, so whatever introduced that probably introduced this bug.
Comment 16•14 years ago
|
||
Comment on attachment 515808 [details] [diff] [review]
fix
r+ with irc nits (explain that FrameIsFinished could just be finishedInInterpreter(), but that we pop the frame directly as an optimization, perhaps rename FrameIsFinished to reflect this distinction).
Attachment #515808 -
Flags: review?(lw) → review+
Assignee | ||
Updated•14 years ago
|
Attachment #515808 -
Flags: approval2.0?
Updated•14 years ago
|
Attachment #515808 -
Flags: approval2.0? → approval2.0+
Assignee | ||
Comment 17•14 years ago
|
||
Whiteboard: [fixed-in-tracemonkey]
Comment 18•14 years ago
|
||
Status: ASSIGNED → RESOLVED
Closed: 14 years ago
Resolution: --- → FIXED
Updated•14 years ago
|
status1.9.1:
--- → unaffected
status1.9.2:
--- → unaffected
Updated•14 years ago
|
Group: core-security
You need to log in
before you can comment on or make changes to this bug.
Description
•