Closed
Bug 563221
Opened 14 years ago
Closed 14 years ago
"Assertion failure: ss->top >= nuses," or "Assertion failure: top != 0,"
Categories
(Core :: JavaScript Engine, defect)
Tracking
()
People
(Reporter: gkw, Assigned: jorendorff)
Details
(Keywords: assertion, regression, testcase, Whiteboard: [sg:nse?] fixed-in-tracemonkey)
Attachments
(1 file, 1 obsolete file)
1.93 KB,
patch
|
brendan
:
review+
|
Details | Diff | Splinter Review |
function f(code) { code = code.replace(/\/\*DUPTRY\d+/, function(k) { n = (k.substr(8)) return g("try{}catch(e){}", n) }) var f = new Function(code) uneval(f) } function g(s, n) { if (n == 1) return s s2 = s + s r = n % 2 d = (n - r) / 2 m = g(s2, d) return r ? s: m } f("if([]){}else if(\"\"()){}else{/*DUPTRY2721}") asserts js debug shell on TM tip without -j at Assertion failure: ss->top >= nuses, at ../jsopcode.cpp:1974
Reporter | ||
Comment 1•14 years ago
|
||
(Not the smallest) regression window: 01012006 js shell does not assert. A 07 Nov 2008 js shell build (changeset http://hg.mozilla.org/tracemonkey/rev/04c360f123e5 ) seems to assert.
Reporter | ||
Comment 2•14 years ago
|
||
function f(code) { code = code.replace(/\/\*DUPTRY\d+/, function(k) { n = (k.substr(8)) return g("try{}catch(e){}", n) }) var f = new Function(code) uneval(f) } function g(s, n) { if (n == 1) return s s2 = s + s r = n % 2 d = (n - r) / 2 m = g(s2, d) return r ? s: m } f("if(x);else if(\"\".*){(n(\"\"()))}else{/*DUPTRY5683}") asserts at Assertion failure: top != 0, at ../jsopcode.cpp:1030 instead
Summary: "Assertion failure: ss->top >= nuses, at ../jsopcode.cpp" → "Assertion failure: ss->top >= nuses," or "Assertion failure: top != 0,"
Reporter | ||
Updated•14 years ago
|
blocking2.0: --- → ?
Assignee | ||
Comment 3•14 years ago
|
||
"" + eval("(function () { if (x) ; else if (y) n(); else { " + Array(5684).join("try{}catch(e){}") + " } });"); flags: LAMBDA NULL_CLOSURE main: 00000: trace 00001: name "x" 00004: ifeq 12 (8) 00007: gotox 102325 (102318) 00012: name "y" 00015: ifeq 31 (16) 00018: callname "n" 00021: call 0 00024: trace 00025: pop 00026: gotox 102325 (102299) 00031: try .... 102325: stop
Assignee | ||
Comment 4•14 years ago
|
||
As usual with these, it's not try/catch-specific. Any large chunk of bytecode will do. "" + eval("(function () { if (x) ; else if (y) n(); else { " + Array(55000).join("e;") + "}});");
Updated•14 years ago
|
Group: core-security
Comment 5•14 years ago
|
||
Hiding for further analysis whether this can be exploited.
Assignee | ||
Comment 6•14 years ago
|
||
Likely exploitable. The decompiler jumps to a bad bytecode offset and continues decompiling.
Assignee | ||
Comment 7•14 years ago
|
||
I think this is the right fix.
Assignee: general → jorendorff
Attachment #443325 -
Flags: review?(brendan)
Comment 8•14 years ago
|
||
Comment on attachment 443325 [details] [diff] [review] v1 Can you explain the fix? This happens because oplen is unsigned? Or what goes on here?
Comment 9•14 years ago
|
||
Comment on attachment 443325 [details] [diff] [review] v1 This leaves oplen possibly wrong, although oplen2 does avoid setting it to an also-possibly-wrong value with which to goto if_again. One line fix is to set oplen = js_CodeSpec[*pc].length; right before the goto if_again (best if immediately after pc += cond; to be precise). /be
Attachment #443325 -
Flags: review?(brendan) → review-
Comment 10•14 years ago
|
||
For DecompileDestructuring, I added LOAD_OP_DATA to get every local depending on an updated pc fetched and up-to-date. But this was back when we used C and hunted with spears and snowshoes (so if the snow was too light we'd fail to catch up to the Moose, and then we'd starve :-/). /be
Comment 11•14 years ago
|
||
I'm pretty sure this is not exploitable. /be
Assignee | ||
Comment 12•14 years ago
|
||
Yes, that's much better.
Attachment #443325 -
Attachment is obsolete: true
Attachment #443408 -
Flags: review?(brendan)
Assignee | ||
Comment 13•14 years ago
|
||
(In reply to comment #8) > (From update of attachment 443325 [details] [diff] [review]) > Can you explain the fix? This happens because oplen is unsigned? Or what goes > on here? oplen is always supposed to be the length of the current opcode. The existing code changes pc (to point to an IFNE/IFEQ opcode) without updating oplen, which still points to a GOTOX opcode. GOTO and IFNE/IFEQ are all the same length (3 bytes), which is why this bug hasn't surfaced before; but GOTOX is 5 bytes. The result was that the decompiler jumped into a bad offset between opcodes and started decompiling. Brendan: Andreas and I have been treating this kind of bug as exploitable for the past few days, since the latest batch of decompiler fuzzbugs. It seems like you'd have to analyze the decompiler pretty closely to make sure it's not possible to use it as an interpreter to do whatever you want to the heap at least.
Comment 14•14 years ago
|
||
The decompiler reads and writes memory, so perhaps there's an exploit, but unlike the rest of the codebase, it has always-on sanity checks that cause it to bail out. We can restrict this bug since it will be hard to prove non-exploitability, but looking at how the optimized shell fails can be illuminating: $ ./Darwin_OPT.OBJ/js js> "" + eval("(function () { if (x) ; else if (y) n(); else { " + Array(10000).join("e;") + " } });"); js> No crash. /be
Comment 15•14 years ago
|
||
Comment on attachment 443408 [details] [diff] [review] v2 Note how the decompiler *writes* only by pushing, and pushing beyond budgeted stack depth fails with OOM: JS_ASSERT(top < StackDepth(ss->printer->script)); if (top >= StackDepth(ss->printer->script)) { JS_ReportOutOfMemory(ss->sprinter.context); return JS_FALSE; } Likewise, underflowing the stack fails: /* ss->top points to the next free slot; be paranoid about underflow. */ top = ss->top; JS_ASSERT(top != 0); if (top == 0) return 0; The sprinter autogrows and checks for malloc failure, so the return 0 may cause overwriting, i.e., invalid decompiler output, but not exploitable memory corruption. I continue to believe that this bug is not exploitable. /be
Attachment #443408 -
Flags: review?(brendan) → review+
Updated•14 years ago
|
Whiteboard: [sg:nse?]
Comment 16•14 years ago
|
||
(In reply to comment #15) > Likewise, underflowing the stack fails: > > /* ss->top points to the next free slot; be paranoid about underflow. */ > top = ss->top; > JS_ASSERT(top != 0); > if (top == 0) > return 0; This means OFF2STR returns the empty string at the base of the sprinter. IOW, pop is a read, not a write. Reading off the end of bytecode could be a privacy leak, although in the case of this bug the bytecode limit pointer (endpc) stops that. Other decompiler bugs have miscalculated endpc, so I thought I'd mention it. /be
Assignee | ||
Comment 17•14 years ago
|
||
Thanks for the explanation. http://hg.mozilla.org/tracemonkey/rev/e4ae029576c1
Whiteboard: [sg:nse?] → [sg:nse?] fixed-in-tracemonkey
Comment 18•14 years ago
|
||
http://hg.mozilla.org/mozilla-central/rev/e4ae029576c1
Status: NEW → RESOLVED
Closed: 14 years ago
Resolution: --- → FIXED
Updated•14 years ago
|
blocking2.0: ? → betaN+
Comment 19•14 years ago
|
||
1.9.1/1.9.2 branches also assert. Given the simple patch and checked-in testcase we might as well fix this on branches.
Updated•14 years ago
|
blocking1.9.1: ? → needed
blocking1.9.2: ? → needed
Updated•14 years ago
|
Group: core-security
Updated•13 years ago
|
blocking1.9.1: needed → -
blocking1.9.2: needed → -
Comment 20•11 years ago
|
||
A testcase for this bug was automatically identified at js/src/tests/js1_8_5/regress/regress-563221.js.
Flags: in-testsuite+
Reporter | ||
Comment 21•11 years ago
|
||
Testcases have been landed by virtue of being marked in-testsuite+ -> VERIFIED as well.
Status: RESOLVED → VERIFIED
You need to log in
before you can comment on or make changes to this bug.
Description
•