Closed
Bug 351102
Opened 18 years ago
Closed 18 years ago
try/catch-guard/finally bytecode still GC-unsafe
Categories
(Core :: JavaScript Engine, defect)
Core
JavaScript Engine
Tracking
()
VERIFIED
FIXED
mozilla1.8.1
People
(Reporter: brendan, Assigned: igor)
References
Details
(Keywords: verified1.8.0.12, verified1.8.1.4, Whiteboard: [sg:critical?])
Attachments
(11 files, 5 obsolete files)
2.44 KB,
text/plain
|
Details | |
2.52 KB,
text/plain
|
Details | |
2.45 KB,
text/plain
|
Details | |
2.34 KB,
text/plain
|
Details | |
2.34 KB,
text/plain
|
Details | |
2.35 KB,
text/plain
|
Details | |
2.53 KB,
text/plain
|
Details | |
19.49 KB,
patch
|
brendan
:
review+
dveditz
:
approval1.8.1.4+
|
Details | Diff | Splinter Review |
19.41 KB,
patch
|
igor
:
review+
|
Details | Diff | Splinter Review |
3.55 KB,
text/plain
|
Details | |
1.27 KB,
patch
|
brendan
:
review+
dveditz
:
approval1.8.0.12+
|
Details | Diff | Splinter Review |
The fix for bug 350312, with followup fix from bug 350837, is not enough. Consider:
function f() {
try {
throw new Error('bad');
} catch (e if (e = null, gc(), false)) {
} catch (e) {
// e is dangling now
}
}
Assignee | ||
Comment 1•18 years ago
|
||
I had to see it. I suspected something was fishy with [throwing], but ignored that :(
Reporter | ||
Comment 2•18 years ago
|
||
Safest way is to stack the exception under the block frame for the catch scope, but it is not compile-time invariant IIRC. Have to psych myself up to re-read the code...
/be
Assignee | ||
Comment 3•18 years ago
|
||
Here is another proof that using cx->exception as an exception storage is not a good idea:
function test()
{
var a = null;
try {
a();
} catch (e) {
}
return false;
}
try {
throw 1;
} catch (e if test()) {
} catch (e if e == 1) {
print("GOOD");
} catch (e) {
print("BAD: "+e);
}
This prints currently:
BAD: TypeError: a is not a function
I think Forth got it right when it uses 2 independent stacks, one for expressions and one for control flow...
Comment 4•18 years ago
|
||
The fuzzer in bug 349611 found this crash:
js> f = function() { try { d.d.d } catch([] if gc()) { } catch(y) { print(y) } }; f(); f();
before 9232, after 9232, break 01008000
Bus error
Is that the same bug?
Comment 6•18 years ago
|
||
Even simpler:
js> try { @foo } catch([] if gc()) { }
before 36928, after 36928, break 01008000
Thread 0 Crashed:
0 <<00000000>> 0x00000000 0 + 0
1 js 0x0004be9c js_ReportUncaughtException + 290 (jsexn.c:1271)
2 js 0x0001a167 JS_ExecuteScript + 113 (jsapi.c:4214)
3 js 0x000029d2 Process + 912 (js.c:268)
4 js 0x00003354 ProcessArgs + 1910 (js.c:494)
5 js 0x00007efd main + 612 (js.c:3159)
6 js 0x000024e6 _start + 216
7 js 0x0000240d start + 41
js> try { d.d.d } catch([] if gc()) { }
before 27696, after 27696, break 01008000
Assertion failure: 3 < (obj)->map->freeslot, at jsexn.c:370
Flags: blocking1.9?
Whiteboard: [sg:critical?]
Updated•18 years ago
|
Assignee: general → igor
Assignee | ||
Comment 7•18 years ago
|
||
One do not even need destructing assignmnet to trigger the bug:
js> try { null.a } catch(e if (e = null, gc())) { }
before 27696, after 27696, break 08a77000
Assertion failure: 3 < (obj)->map->freeslot, at jsexn.c:370
Status: NEW → ASSIGNED
Assignee | ||
Comment 8•18 years ago
|
||
This is an untested patch to show the main idea behind the fix.
As the test cases for this bug demonstrates, one can not use cx->exception as a temporary storage for the pending exception while executing the destructing assignment/checking the guards for 2 reasons:
1. This is GC unsafe since cx->exception is no longer marked as throwing and will be GC-ed if the guard can unroot it.
2. The destructing assignment through element getters or the guard can run arbitrary code that can throw and catch other exceptions overriding the original cx->exception. Here is an example demonstrating that:
~/m/trunk/mozilla/js/src $ cat ~/s/y.js
var obj = { get a() {
try {
throw 1;
} catch (e) {
}
return false;
}};
try {
throw obj;
} catch ({a: a} if a) {
throw "Unreachable";
} catch (e) {
if (e !== obj)
throw "Unexpected exception: "+uneval(e);
}
~/m/trunk/mozilla/js/src $ ./Linux_All_DBG.OBJ/js ~/s/y.js
uncaught exception: Unexpected exception: 1
To fix this the patch adds an extra dup/pop code for any catch with the guard to preserve the original exception. That required to change jsop_throwing to pop the exception and store it in cx->exception.
With this gosub/retsub no longer needs to push/restore cx->throwing state and rethrowing after the failed guard is simplified.
Assignee | ||
Comment 9•18 years ago
|
||
Compared with the previous version, this fixes the decompiler to ignore the extra [dup] for the guard, simplify the rethrowing after the guards code just to [throw] and bumps the xdr version as the patch changes the semantic of the bytecode.
Attachment #258877 -
Attachment is obsolete: true
Assignee | ||
Comment 10•18 years ago
|
||
In the new version I restored pushing/popping 2 stack slots in [gosub]/[retsub] to ensure that finally handler begins with a consistent stack budget. Without it any [setsp] executed inside finally that is invoked during normal non-throwing execution will create a hole on the stack and bring into the root set a potentially already GC-ed value.
Attachment #258894 -
Attachment is obsolete: true
Assignee | ||
Comment 11•18 years ago
|
||
The new version cleanups [exception] decompilation as that bytecode is only generated as a part of catch code.
The test suite shows no regression with the patch.
Attachment #258898 -
Attachment is obsolete: true
Attachment #258901 -
Flags: review?(brendan)
Reporter | ||
Comment 12•18 years ago
|
||
Comment on attachment 258901 [details] [diff] [review]
Fix v4
>? throwing
>Index: jsemit.c
>===================================================================
>RCS file: /cvsroot/mozilla/js/src/jsemit.c,v
>retrieving revision 3.240
>diff -U7 -p -r3.240 jsemit.c
>--- jsemit.c 11 Mar 2007 19:25:58 -0000 3.240
>+++ jsemit.c 17 Mar 2007 13:39:11 -0000
>@@ -4830,20 +4830,23 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
> jsint count = 0; /* previous catch block's population */
>
> catchStart = end;
>
> /*
> * The emitted code for a catch block looks like:
> *
>+ * [ trowing ] only if 2nd+ catch block
Typo: throwing
>+ * Rethrow the exception delegating executing of finally if any
>+ * to the exception handler.
Comma before "delegating"
>+ * We emit a [setsp][gosub] sequence for running finally code
>+ * while letting an uncaught exception pass thrown from within
>+ * the try in a try-finally. The [gosub] and [retsub] opcodes
>+ * will take care of stacking and rethrowing any exception pending
The "pass thrown" adjacent words seem ungrammatical or just hard to read. Reword?
>+++ jsgc.c 17 Mar 2007 13:39:12 -0000
>@@ -2960,16 +2960,21 @@ restart:
> /* Cleanup temporary "dormant" linkage. */
> if (acx->fp)
> acx->fp->dormantNext = NULL;
>
> /* Mark other roots-by-definition in acx. */
> GC_MARK(cx, acx->globalObject, "global object");
> MarkWeakRoots(cx, &acx->weakRoots);
>- if (acx->throwing && JSVAL_IS_GCTHING(acx->exception))
>- GC_MARK(cx, JSVAL_TO_GCTHING(acx->exception), "exception");
>+ if (acx->throwing) {
>+ if (JSVAL_IS_GCTHING(acx->exception))
>+ GC_MARK(cx, JSVAL_TO_GCTHING(acx->exception), "exception");
>+ } else {
>+ /* Avoid leaving GC-ed junk in the exception object. */
>+ acx->exception = JSVAL_NULL;
Nit: "exception value" not "exception object".
>+++ jsinterp.c 17 Mar 2007 13:39:14 -0000
>@@ -5360,15 +5360,20 @@ interrupt:
> i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUB_LENGTH;
> len = GET_JUMP_OFFSET(pc);
> PUSH(INT_TO_JSVAL(i));
> END_VARLEN_CASE
>
> BEGIN_CASE(JSOP_GOSUBX)
> JS_ASSERT(cx->exception != JSVAL_HOLE);
>- lval = cx->throwing ? cx->exception : JSVAL_HOLE;
>+ if (!cx->throwing) {
>+ lval = JSVAL_HOLE;
>+ } else {
>+ lval = cx->exception;
>+ cx->throwing = JS_FALSE;
>+ }
> PUSH(lval);
> i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUBX_LENGTH;
> len = GET_JUMPX_OFFSET(pc);
> PUSH(INT_TO_JSVAL(i));
> END_VARLEN_CASE
Where's the corresponding change for JSOP_GOSUB?
> case JSOP_EXCEPTION:
>- /*
>- * The only other JSOP_EXCEPTION cases occur as part of a code
>- * sequence that follows a SRC_CATCH-annotated JSOP_ENTERBLOCK
>- * or that precedes a SRC_HIDDEN-annotated JSOP_POP emitted
>- * when returning from within a finally clause.
>- */
>- sn = js_GetSrcNote(jp->script, pc);
>- LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_HIDDEN);
>- todo = -2;
>+ /* The catch decompiler handles it itself. */
>+ LOCAL_ASSERT(JS_FALSE);
Nit: "handles this op itself."
>@@ -2381,15 +2374,25 @@ Decompile(SprintStack *ss, jsbytecode *p
> jp->indent -= 4;
> js_printf(CLEAR_MAYBE_BRACE(jp), "\t} catch (");
>
> pc2 = pc;
> pc += oplen;
> LOCAL_ASSERT(*pc == JSOP_EXCEPTION);
> pc += JSOP_EXCEPTION_LENGTH;
>-
>+ if (*pc == JSOP_DUP) {
>+ sn2 = js_GetSrcNote(jp->script, pc);
>+ if (sn2) {
>+ /*
>+ * dup pushing the exception object to use after
Nit: "A dup that pushes the exception object ...."
/be
Assignee | ||
Comment 13•18 years ago
|
||
(In reply to comment #12)
> Where's the corresponding change for JSOP_GOSUB?
The current code in the interpreter contains:
BEGIN_CASE(JSOP_GOSUB)
JS_ASSERT(cx->exception != JSVAL_HOLE);
if (!cx->throwing) {
lval = JSVAL_HOLE;
} else {
lval = cx->exception;
cx->throwing = JS_FALSE;
}
PUSH(lval);
i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUB_LENGTH;
len = GET_JUMP_OFFSET(pc);
PUSH(INT_TO_JSVAL(i));
END_VARLEN_CASE
BEGIN_CASE(JSOP_GOSUBX)
JS_ASSERT(cx->exception != JSVAL_HOLE);
lval = cx->throwing ? cx->exception : JSVAL_HOLE;
PUSH(lval);
i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUBX_LENGTH;
len = GET_JUMPX_OFFSET(pc);
PUSH(INT_TO_JSVAL(i));
END_VARLEN_CASE
That is, compared with JSOP_GOSUB, its X-version does not clear cx->throwing flag. The patch just fixes that.
Assignee | ||
Comment 14•18 years ago
|
||
Here is the previous patch with the nits addressed.
Attachment #258901 -
Attachment is obsolete: true
Attachment #259064 -
Flags: review?(brendan)
Attachment #258901 -
Flags: review?(brendan)
Reporter | ||
Comment 15•18 years ago
|
||
Comment on attachment 259064 [details] [diff] [review]
Fix v4b
Ah, I should have looked. Didn't think we missed that back in the rush to patch the branch, but we did.
r=me, thanks.
/be
Attachment #259064 -
Flags: review?(brendan) → review+
Reporter | ||
Comment 16•18 years ago
|
||
Comment on attachment 259064 [details] [diff] [review]
Fix v4b
I recall writing a testcase generator -- does it have complete coverage? If so, this patch or a backported version of it should go into the next branch releases.
/be
Comment 17•18 years ago
|
||
(In reply to comment #16)
I believe you are referring to js1_5/extensions/regress-350312-0[23].js.
Assignee | ||
Comment 18•18 years ago
|
||
I committed the patch from comment 14 to the trunk:
Checking in jsemit.c;
/cvsroot/mozilla/js/src/jsemit.c,v <-- jsemit.c
new revision: 3.242; previous revision: 3.241
done
Checking in jsgc.c;
/cvsroot/mozilla/js/src/jsgc.c,v <-- jsgc.c
new revision: 3.207; previous revision: 3.206
done
Checking in jsinterp.c;
/cvsroot/mozilla/js/src/jsinterp.c,v <-- jsinterp.c
new revision: 3.341; previous revision: 3.340
done
Checking in jsopcode.c;
/cvsroot/mozilla/js/src/jsopcode.c,v <-- jsopcode.c
new revision: 3.218; previous revision: 3.217
done
Checking in jsopcode.tbl;
/cvsroot/mozilla/js/src/jsopcode.tbl,v <-- jsopcode.tbl
new revision: 3.90; previous revision: 3.89
done
Checking in jsxdrapi.h;
/cvsroot/mozilla/js/src/jsxdrapi.h,v <-- jsxdrapi.h
new revision: 1.28; previous revision: 1.27
done
Status: ASSIGNED → RESOLVED
Closed: 18 years ago
Resolution: --- → FIXED
Assignee | ||
Comment 19•18 years ago
|
||
(In reply to comment #16)
> (From update of attachment 259064 [details] [diff] [review])
> I recall writing a testcase generator -- does it have complete coverage? If so,
> this patch or a backported version of it should go into the next branch
> releases.
If a backport for 1.8.0 branch would require too much efforts, I suggest just apply the jsgc.c part of the patch there which replaces cx->exception with JSVAL_NULL after GC if not throwing. This is enough to ensure GC-safty.
Flags: blocking1.8.1.4?
Flags: blocking1.8.0.12?
Assignee | ||
Comment 20•18 years ago
|
||
(In reply to comment #17)
> I believe you are referring to js1_5/extensions/regress-350312-0[23].js.
>
Note that definitely does not include the test case from comment 8.
Updated•18 years ago
|
Flags: blocking1.8.1.4?
Flags: blocking1.8.1.4+
Flags: blocking1.8.0.12?
Flags: blocking1.8.0.12+
Assignee | ||
Comment 21•18 years ago
|
||
Fixing proper regression dependancies
Comment 22•18 years ago
|
||
(In reply to comment #6)
try { try { @foo } catch([] if gc()) { } } catch(ex) {}
try { try { d.d.d } catch([] if gc()) { } } catch(ex) {}
each give (with or without setting version(170) btw)
Assertion failure: !fp->blockChain || OBJ_GET_PARENT(cx, obj) == fp->blockChain, at jsinterp.c:5837
which is "somewhat" similar to Bug 361566 and Bug 355832 except it doesn't require js1_7. Note the unwrapped original versions no longer assert though.
Flags: in-testsuite+
Comment 23•18 years ago
|
||
Comment 24•18 years ago
|
||
Comment 25•18 years ago
|
||
Comment 26•18 years ago
|
||
Comment 27•18 years ago
|
||
Comment 28•18 years ago
|
||
Comment 29•18 years ago
|
||
Updated•18 years ago
|
Attachment #259859 -
Attachment is patch: false
Assignee | ||
Comment 30•18 years ago
|
||
(In reply to comment #22)
> (In reply to comment #6)
>
> try { try { @foo } catch([] if gc()) { } } catch(ex) {}
> try { try { d.d.d } catch([] if gc()) { } } catch(ex) {}
>
> each give (with or without setting version(170) btw)
>
> Assertion failure: !fp->blockChain || OBJ_GET_PARENT(cx, obj) ==
> fp->blockChain, at jsinterp.c:5837
This is unrelated to this GC-safty bug. Just replacing gc() with false gives the same error:
try { try { throw 1 } catch([] if false) { } } catch(ex) {}
Assertion failure: !fp->blockChain || OBJ_GET_PARENT(cx, obj) == fp->blockChain, at jsinterp.c:5853
Comment 31•18 years ago
|
||
Ok, I split that into bug 375695.
Updated•18 years ago
|
Flags: wanted1.8.1.x+
Flags: wanted1.8.0.x+
Assignee | ||
Comment 32•18 years ago
|
||
The patch and the follow up regression fixes from bug 351102 applies to 1.8 branch as is with the exception of changes to bump xdrversion. For the branch I set to 10 to ensure the uniqueness as the patch induces format changes that does not correspond to anything on the trunk.
Attachment #260876 -
Flags: review?(brendan)
Attachment #260876 -
Flags: approval1.8.1.4?
Updated•18 years ago
|
Whiteboard: [sg:critical?] → [sg:critical?] need r=brendan
Assignee | ||
Comment 33•18 years ago
|
||
Pinging: given the fast approaching freeze for the next releases I would like to get r=something for 1.8.1 patch to base 1.8.0 version on it.
Reporter | ||
Comment 34•18 years ago
|
||
Comment on attachment 260876 [details] [diff] [review]
Fix for 1.8 branch including the regression fixes
Sorry, lost track of the request -- looks right (branch vs. trunk diff with this applied would be helpful to confirm).
/be
Attachment #260876 -
Flags: review?(brendan) → review+
Comment 35•18 years ago
|
||
I've moved the -03,-04,-05,-07 tests to js1_7/extensions since they cause syntax errors in 1.8.0. -04, -05 still show the assert in bug 375695. verifying on trunk though.
Status: RESOLVED → VERIFIED
Assignee | ||
Comment 36•18 years ago
|
||
This is for references purposes the trubk patch + trunk fixes to simplify compare with versions for branches.
Attachment #259064 -
Attachment is obsolete: true
Attachment #261391 -
Flags: review+
Assignee | ||
Comment 37•18 years ago
|
||
I did not include this diff initially as patch applied cleanly with the exception of XDR version code. But for the references here is the diff.
Now I need to backporty to 1.8.0 where the real differences lives.
Reporter | ||
Comment 38•18 years ago
|
||
Thanks -- looking fwd to the 1.8.0 diff of diffs -- one trick I use is to grep out context jitter (lines without + or - in the input diff).
/be
Assignee | ||
Comment 39•18 years ago
|
||
Given that just a single hunk from trunk/1.8.1 patches applies to 1.8.0 and a prototype re-implementation of the patch on 1.8.0 is rather non-trivial and non-minimal, I suggest to just fix just GC-hazard for 1.8.0 via nulling cx->exception after GC.
Attachment #261470 -
Flags: review?(brendan)
Attachment #261470 -
Flags: approval1.8.0.12?
Assignee | ||
Updated•18 years ago
|
Attachment #261470 -
Attachment is patch: true
Attachment #261470 -
Attachment mime type: text/x-patch → text/plain
Reporter | ||
Comment 40•18 years ago
|
||
Comment on attachment 261470 [details] [diff] [review]
just fixing GC hazard for 1.8.0
Good idea -- we are almost at end of life for 1.8.0 so r=me.
/be
Attachment #261470 -
Flags: review?(brendan) → review+
Comment 41•18 years ago
|
||
Comment on attachment 261470 [details] [diff] [review]
just fixing GC hazard for 1.8.0
approved for 1.8.0.12, a=dveditz for release-drivers
Attachment #261470 -
Flags: approval1.8.0.12? → approval1.8.0.12+
Comment 42•18 years ago
|
||
Comment on attachment 260876 [details] [diff] [review]
Fix for 1.8 branch including the regression fixes
approved for 1.8.1.4, a=dveditz for release-drivers
Attachment #260876 -
Flags: approval1.8.1.4? → approval1.8.1.4+
Assignee | ||
Comment 43•18 years ago
|
||
I committed the patch from comment 32 to MOZILLA_1_8_BRANCH:
Checking in jsemit.c;
/cvsroot/mozilla/js/src/jsemit.c,v <-- jsemit.c
new revision: 3.128.2.71; previous revision: 3.128.2.70
done
Checking in jsgc.c;
/cvsroot/mozilla/js/src/jsgc.c,v <-- jsgc.c
new revision: 3.104.2.33; previous revision: 3.104.2.32
done
Checking in jsinterp.c;
/cvsroot/mozilla/js/src/jsinterp.c,v <-- jsinterp.c
new revision: 3.181.2.88; previous revision: 3.181.2.87
done
Checking in jsopcode.c;
/cvsroot/mozilla/js/src/jsopcode.c,v <-- jsopcode.c
new revision: 3.89.2.71; previous revision: 3.89.2.70
done
Checking in jsopcode.tbl;
/cvsroot/mozilla/js/src/jsopcode.tbl,v <-- jsopcode.tbl
new revision: 3.43.2.22; previous revision: 3.43.2.21
done
Checking in jsxdrapi.h;
/cvsroot/mozilla/js/src/jsxdrapi.h,v <-- jsxdrapi.h
new revision: 1.14.58.11; previous revision: 1.14.58.10
done
Keywords: fixed1.8.1.4
Assignee | ||
Comment 44•18 years ago
|
||
I committed the patch from comment 39 to MOZILLA_!_8_0_BRANCH:
Checking in jsgc.c;
/cvsroot/mozilla/js/src/jsgc.c,v <-- jsgc.c
new revision: 3.104.2.3.2.19; previous revision: 3.104.2.3.2.18
done
Keywords: fixed1.8.0.12
Assignee | ||
Updated•18 years ago
|
Whiteboard: [sg:critical?] need r=brendan → [sg:critical?]
Comment 45•18 years ago
|
||
verified fixed windows, macppc, linux 1.8.0, 1.8.1 20070417 modulo asserts in bug 375695.
Updated•18 years ago
|
Group: security
Comment 46•18 years ago
|
||
/cvsroot/mozilla/js/tests/js1_5/extensions/regress-351102-01.js,v <-- regress-351102-01.js
initial revision: 1.1
/cvsroot/mozilla/js/tests/js1_5/extensions/regress-351102-02.js,v <-- regress-351102-02.js
initial revision: 1.1
/cvsroot/mozilla/js/tests/js1_5/extensions/regress-351102-06.js,v <-- regress-351102-06.js
initial revision: 1.1
Comment 47•18 years ago
|
||
/cvsroot/mozilla/js/tests/js1_7/extensions/regress-351102-03.js,v <-- regress-351102-03.js
initial revision: 1.1
/cvsroot/mozilla/js/tests/js1_7/extensions/regress-351102-04.js,v <-- regress-351102-04.js
initial revision: 1.1
/cvsroot/mozilla/js/tests/js1_7/extensions/regress-351102-05.js,v <-- regress-351102-05.js
initial revision: 1.1
/cvsroot/mozilla/js/tests/js1_7/extensions/regress-351102-07.js,v <-- regress-351102-07.js
initial revision: 1.1
done
You need to log in
before you can comment on or make changes to this bug.
Description
•