Note: There are a few cases of duplicates in user autocompletion which are being worked on.

Yet another GC hazard with ++/-- in js_Interpret

VERIFIED FIXED in mozilla1.9

Status

()

Core
JavaScript Engine
P2
critical
VERIFIED FIXED
10 years ago
9 years ago

People

(Reporter: Igor Bukanov, Assigned: Igor Bukanov)

Tracking

({verified1.8.1.15})

unspecified
mozilla1.9
verified1.8.1.15
Points:
---
Bug Flags:
blocking1.9 +
blocking1.8.1.13 -
blocking1.8.1.15 +
wanted1.8.1.x +
blocking1.8.0.next +
in-testsuite +
in-litmus -

Firefox Tracking Flags

(Not tracked)

Details

(Whiteboard: [sg:critical?] trunk fixed in bug 418641)

Attachments

(3 attachments, 7 obsolete attachments)

(Assignee)

Description

10 years ago
The current implementation of ++ and -- operators in js_Interpret to increase/decrease indexed property of an object does not root the id of the property. This leads to a GC hazard when code tries to save the result back into object as the following example demonstrates:

~/m/ff/mozilla/js/src $ cat ~/m/y.js
var obj = {};
var id = { toString: function() { return ""+Math.pow(2, 0.1); } }
obj[id] = { valueOf: unrooter };
print(obj[id]++);
gc();
print(uneval(obj));

function unrooter()
{
    delete obj[id];
    gc();
    return 10;
}
~/m/ff/mozilla/js/src $ ~/m/ff/mozilla/js/src/Linux_All_OPT.OBJ/js ~/m/y.js
before 16384, after 16384, break 09ffc000
10
before 16384, after 16384, break 09ffc000
segmentation fault
(Assignee)

Comment 1

10 years ago
There is another hazard in the ++/-- implementation. The code in js_Interpret that stores the result contains:

            ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
...
            PUSH_OPND(rtmp);

Here for pre "++" and "--" operations 

   rval == rtmp == incremenetd_or_decremented_value. 

When rval does not fit the int range, it will be a newborn double representing the result. So this code assumes that after the property setter returns, rval still will be alive GC thing that can be stored back to the stack. But this is not true in general. Consider array_length_setter from jsarray.c. It contains the following code:

    if (!ValueIsLength(cx, *vp, &newlen))
        return JS_FALSE;
    if (!js_GetLengthProperty(cx, obj, &oldlen))
        return JS_FALSE;
    if (oldlen > newlen) {
        if (oldlen - newlen < (1 << 24)) {
            do {
                --oldlen;
                if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) ||
                    !DeleteArrayElement(cx, obj, oldlen)) {
                    return JS_FALSE;
                }
            } while (oldlen != newlen);
...
    }
    if (!IndexToValue(cx, newlen, vp))
        return JS_FALSE;
    STOBJ_SET_SLOT(obj, JSSLOT_ARRAY_LENGTH, *vp);
 
Here vp is &rval. When oldlen > newlen, the code can call operational callback. That in turn can call the full GC collecting the context of vp. 
In such situation when the setter returns, rtemp, the original value of rval, will refer to the collected value and the code will push it back into JS stack. I.e., if the operation callback runs the GC, then the following example will store the dead value into x:

var a = Array(1 << 30 + 1);
var x = --a.length;

Note that there is no hazard in the array code as vp is never read again after a potential GC call. It is precisely the ++/-- implementation that introduces the hazard when it accesses weakly rooted value after calling a setter.
(Assignee)

Updated

10 years ago
Flags: blocking1.9?
Flags: blocking1.8.1.13?

Updated

10 years ago
Flags: blocking1.9? → blocking1.9+
Target Milestone: --- → mozilla1.9beta4
(Assignee)

Comment 2

10 years ago
Created attachment 304256 [details] [diff] [review]
v1

Untested patch. To fix the first hazard, the patch always roots the result of FETCH_ELEMT_ID. To fix the second hazard the patch makes sure preincrements also includes JOF_TMPSLOT.

Besides the fix the patch also moves non-int increment and decrement to separated functions to share and simplify the code for this infrequently taken path. This shrinks the size of stripped jsinterp.o on Linux by 2K.

The patch is not tested.
(Assignee)

Updated

10 years ago
Blocks: 418641
(Assignee)

Comment 3

10 years ago
I filed bug 418641 as a cover bug for this.
No longer blocks: 418641
Flags: wanted1.8.1.x+
Whiteboard: [sg:critical?]
(Assignee)

Comment 4

10 years ago
The new patch from bug 418641 comment 4 fixes this. It never passes pointers to unrooted values to getters/setters. This may seems excessive since the rest of code still passes &rval/&rtmp to the native functions. But given the history of hazards, I prefer not to rely on weak roots at all. 

Plus in fact &rval/&rtmp are expensive code wise since they force the compiler to assume that rval/rtmp can be modified by arbitrary native functions. 

On the other hand &sp is not exposed so sp lives most of the time in registers. Thus passing &rval and &sp[-1] and using rval or sp[-1] generates the same amount of code. In the former case the compiler will use native_stack_register[offset_for_rval] and in the latter case  register_storing_sp[-1].
Patch is kinda big and scary... will this land on the trunk anytime soon so we can check for regressions?
(Assignee)

Comment 6

10 years ago
(In reply to comment #5)
> Patch is kinda big and scary... will this land on the trunk anytime soon so we
> can check for regressions?

Note that the branch would require rather different patch as there rooting id is not trivial. So I will go there for a minimal patch that uses JS_KEEP_ATOMS for rooting the id and will apply JSOF_TEMPSLOT for index and property operations. But I do plan to replace the main macro implementing ++/-- for non-integer cases by a function to follow the trunk.

Moving bugs that aren't beta 4 blockers to target final release.
Target Milestone: mozilla1.9beta4 → mozilla1.9

Updated

10 years ago
Flags: tracking1.9+ → blocking1.9+
Priority: -- → P2
Since the trunk patch hasn't been reviewed or landed on trunk we'll likely have to wait for next branch release to catch this one. Minusing for '13
Flags: blocking1.8.1.14?
Flags: blocking1.8.1.13?
Flags: blocking1.8.1.13-
(Assignee)

Comment 9

10 years ago
This is fixed on the trunk since landing bug 418641.
Status: NEW → RESOLVED
Last Resolved: 10 years ago
Resolution: --- → FIXED
Created attachment 312529 [details]
js1_5/GC/regress-418128.js
Flags: in-testsuite+
Flags: in-litmus-
v 1.9.0
Status: RESOLVED → VERIFIED
(In reply to comment #9)
> This is fixed on the trunk since landing bug 418641.

Does that mean the patch in this bug is obsolete and we should look at bug 418641 for the 1.8 branch, or that this patch was checked in when that one was?
Whiteboard: [sg:critical?] → [sg:critical?] fixed by bug 418641 ?
(Assignee)

Comment 13

9 years ago
(In reply to comment #12)
> (In reply to comment #9)
> > This is fixed on the trunk since landing bug 418641.
> 
> Does that mean the patch in this bug is obsolete and we should look at bug
> 418641 for the 1.8 branch, or that this patch was checked in when that one was?

The branch requires a new separated patch. The patch in this bug or in the bug  418641 can serve only as a guideline.
(Assignee)

Updated

9 years ago
Attachment #304256 - Attachment is obsolete: true
Flags: blocking1.8.1.15? → blocking1.8.1.15+
Whiteboard: [sg:critical?] fixed by bug 418641 ? → [sg:critical?] fixed in bug 418641
(Assignee)

Comment 14

9 years ago
Created attachment 323761 [details] [diff] [review]
fix for 1.8 branch

This is what I am going to test on 2008-06-04 late evening PST. Still I am asking for an early review to make sure that I have not missed anything in the patch.

To solve the problem with unrooted id the patch uses JS_KEEP_ATOMS. For the problem of the setter unrooting the pre-increment result the patch makes sure that an extra temporary slot is allocated both for post- and pre-increment.
Attachment #323761 - Flags: review?(brendan)
Comment on attachment 323761 [details] [diff] [review]
fix for 1.8 branch

Looks good. Assuming it all tests well too. r=me.

/be
Attachment #323761 - Flags: review?(brendan)
Attachment #323761 - Flags: review+
Attachment #323761 - Flags: approval1.8.1.15?
Comment on attachment 323761 [details] [diff] [review]
fix for 1.8 branch

Approved for 1.8.1.15, a=dveditz for release-drivers
Attachment #323761 - Flags: approval1.8.1.15? → approval1.8.1.15+
(Assignee)

Comment 17

9 years ago
Comment on attachment 323761 [details] [diff] [review]
fix for 1.8 branch

The patch regressed the js shell test suite.
Attachment #323761 - Attachment is obsolete: true
Attachment #323761 - Flags: approval1.8.1.15+
(Assignee)

Comment 18

9 years ago
Created attachment 324028 [details] [diff] [review]
fix for 1.8 branch

Now the patch passes shell tests without regressions: in the previous one I "optimized" ++/-- for var and args too much.

To Bob Clary: is there a slow test list for 1.8.1 branch? When I run the tests on sm-valgrind01 with -L slow-n.js it takes 3 hours or so.
Attachment #312529 - Attachment is obsolete: true
Attachment #324028 - Flags: review?(brendan)
(Assignee)

Comment 19

9 years ago
Created attachment 324029 [details] [diff] [review]
fix for 1.8 branch v2 for real

The previous attachment has a wrong patch
Attachment #324028 - Attachment is obsolete: true
Attachment #324029 - Flags: review?(brendan)
Attachment #324028 - Flags: review?(brendan)
(Assignee)

Updated

9 years ago
Attachment #324029 - Attachment is patch: true
Attachment #324029 - Attachment mime type: application/octet-stream → text/plain
(In reply to comment #18)

> 
> To Bob Clary: is there a slow test list for 1.8.1 branch? When I run the tests
> on sm-valgrind01 with -L slow-n.js it takes 3 hours or so.

No, but I can create branch specific versions. Are you using jsDriver with the default timeout? It is rather long at 3600 seconds. -T 480 or -T 120 will cut it down to more manageable period.
Whiteboard: [sg:critical?] fixed in bug 418641 → [sg:critical?] need branch r=brendan; trunk fixed in bug 418641
Comment on attachment 324029 [details] [diff] [review]
fix for 1.8 branch v2 for real

Sorry I missed that!

/be
Attachment #324029 - Flags: review?(brendan)
Attachment #324029 - Flags: review+
Attachment #324029 - Flags: approval1.8.1.15?
Whiteboard: [sg:critical?] need branch r=brendan; trunk fixed in bug 418641 → [sg:critical?] trunk fixed in bug 418641
Comment on attachment 324029 [details] [diff] [review]
fix for 1.8 branch v2 for real

Approved for 1.8.1.15, a=dveditz for release-drivers
Attachment #324029 - Flags: approval1.8.1.15? → approval1.8.1.15+
(Assignee)

Comment 23

9 years ago
I checked in the patch from the comment 19 to the MOZILLA_1_8_BRANCH:

Checking in jsemit.c;
/cvsroot/mozilla/js/src/jsemit.c,v  <--  jsemit.c
new revision: 3.128.2.77; previous revision: 3.128.2.76
done
Checking in jsinterp.c;
/cvsroot/mozilla/js/src/jsinterp.c,v  <--  jsinterp.c
new revision: 3.181.2.101; previous revision: 3.181.2.100
done
Keywords: fixed1.8.1.15
verified fixed 1.8.1.15 linux/mac/win
Keywords: fixed1.8.1.15 → verified1.8.1.15

Comment 25

9 years ago
Created attachment 325247 [details] [diff] [review]
1.8.0 attempt

Comment 26

9 years ago
Comment on attachment 325247 [details] [diff] [review]
1.8.0 attempt

the other option would have been to pull in bug 352272 - however, thats 1.7 from what i can tell; thus this backport.

Anyway, it fixes the crash for me too.
Attachment #325247 - Flags: review?(igor)

Comment 27

9 years ago
Created attachment 325248 [details] [diff] [review]
1.8 to 1.8.0 plaindiff

Comment 28

9 years ago
Without patch:
js> before 7157, after 6409, break 0070e000
Segmentation fault
Flags: blocking1.8.0.15+

Comment 29

9 years ago
Comment on attachment 325247 [details] [diff] [review]
1.8.0 attempt

we found a glitch, will attach updated one
Attachment #325247 - Flags: review?(igor) → review-

Comment 30

9 years ago
Created attachment 325249 [details] [diff] [review]
 1.8.0 attempt 2

2nd attempt
Attachment #325247 - Attachment is obsolete: true
Attachment #325248 - Attachment is obsolete: true
Attachment #325249 - Flags: review?(igor)

Comment 31

9 years ago
Created attachment 325250 [details] [diff] [review]
 1.8 to 1.8.0 plaindiff
(Assignee)

Comment 32

9 years ago
Comment on attachment 325249 [details] [diff] [review]
 1.8.0 attempt 2

>Index: jsemit.c

>         if (pn2->pn_type != TOK_NAME &&
>-            (js_CodeSpec[op].format & JOF_POST) &&
>+            (js_CodeSpec[op].format & (JOF_INC | JOF_DEC)) &&
>             (uintN)++cg->stackDepth > cg->maxStackDepth) {
>             cg->maxStackDepth = cg->stackDepth;

...
> 
>-        if (pn2->pn_type != TOK_NAME && (js_CodeSpec[op].format & JOF_POST))
>+        if (pn2->pn_type != TOK_NAME && (js_CodeSpec[op].format & (JOF_INC | JOF_DEC)))
>             --cg->stackDepth;

Right, on 1.8 there was only one place to patch as there the first "if" affected only cg->maxStackDepth without touching cg->stackDepth. On 1.8.0 both places has to be patched.

>Index: jsinterp.c
>             /* The operand must contain a number. */
>+            /* Preload for use in the if/else immediately below. */
>+            cs = &js_CodeSpec[op];
>+

On 1.8.0 branch cs is always initialized before interpreter's switch. There is no need to assign it again and these 3 added lines can be removed. r+ with this fixed.
Attachment #325249 - Flags: review?(igor)

Comment 33

9 years ago
Created attachment 325262 [details] [diff] [review]
1.8.0 attempt 2 + comments
Attachment #325249 - Attachment is obsolete: true
(Assignee)

Updated

9 years ago
Attachment #325262 - Flags: review+
Group: security
/cvsroot/mozilla/js/tests/js1_5/GC/regress-418128.js,v  <--  regress-418128.js
initial revision: 1.1

changeset:   15651:33dac11fb9ed
You need to log in before you can comment on or make changes to this bug.