Closed Bug 516897 Opened 15 years ago Closed 15 years ago

64bit-only crash [@ GetGCThingFlags] [@ JS_CallTracer] with setter, watch, gc

Categories

(Core :: JavaScript Engine, defect)

x86_64
macOS
defect
Not set
critical

Tracking

()

RESOLVED FIXED
mozilla1.9.3a1
Tracking Status
status1.9.2 --- beta1-fixed

People

(Reporter: jruderman, Assigned: mrbkap)

References

Details

(Keywords: crash, testcase, Whiteboard: fixed-in-tracemonkey)

Crash Data

Attachments

(1 file)

Using a 64-bit js shell build on 32-bit Leopard, this crashes.  (opt or debug, with or without -j.)

this.__defineSetter__("x", gc);
this.watch("x",function(){return});
x = 3;

It usually dereferences 0x0fe0 or 0x000c, but I don't trust GC, so I'm filing as security-sensitive.  Oh, and sometimes it divides by 0 instead.

As usual, I'm curious why this is 64bit-only.  I used dvander's build instructions from bug 489146 comment 27.
This happens on 32-bit too - it just doesn't crash. 

(gdb) f
#0  js_TraceStackFrame (trc=0xbfffe8c4, fp=0xbfffec3c) at ../jsgc.cpp:2856
2856	            TRACE_JSVALS(trc, nslots, fp->slots, "slot");
(gdb) p nslots
$3 = 268436698

The GC is reading totally invalid memory because js_watch_set decided to set sp = NULL.
Whoops, and meant to also show the computation that goes wrong:

2845	        if (fp->slots) {
(gdb) 
2850	            if (fp->regs) {
(gdb) 
2851	                nslots = (uintN) (fp->regs->sp - fp->slots);
(Typed most of this before comment 1, posting for general edumacation in case it helps)

2850         /* fp->slots is null for watch pseudo-frames, see js_watch_set. */
2851         if (fp->slots) {
2852             /*
2853              * Don't mark what has not been pushed yet, or what has been
2854              * popped already.
2855              */
2856             if (fp->regs) {
2857                 nslots = (uintN) (fp->regs->sp - fp->slots);
2858                 JS_ASSERT(nslots >= fp->script->nfixed);
2859             } else {
2860                 nslots = fp->script->nfixed;
2861             }
2862             TRACE_JSVALS(trc, nslots, fp->slots, "slot");
2863         }

fp->regs->sp is NULL, fp->slots is valid.  The subtraction on 32-bit produces some monster number for nslots, of course greater than fp->script->nfixed.  Why doesn't 32-bit crash?

2822 #define TRACE_JSVALS(trc, len, vec, name)                      \
2823     JS_BEGIN_MACRO                                             \
2824     jsval _v, *_vp, *_end;                                     \
2825                                                                \
2826         for (_vp = vec, _end = _vp + len; _vp < _end; _vp++) { \
2827             _v = *_vp;                                         \
2828             if (JSVAL_IS_TRACEABLE(_v)) {                      \
2829                 JS_SET_TRACING_INDEX(trc, name, _vp - (vec));  \
2830                 JS_CallTracer(trc, JSVAL_TO_TRACEABLE(_v),     \
2831                               JSVAL_TRACE_KIND(_v));           \
2832             }                                                  \
2833         }                                                      \
2834     JS_END_MACRO

(Column-79-aligned continuation characters are killing me here, I had to realign the entire thing!)

|_vp = fp->slots, _end = fp->slots + nslots| but |nslots == (fp->regs->sp - fp->slots)| on 32-bit, so |_end = fp->slots + fp->regs->sp - fp->slots| and therefore |_end = fp->slots|, so the loop halts immediately.  Of course, on 64-bit |0 - pointer| produces bignum, and the truncation to 32-bit kills the alignment of the stars that happens on 32-bit.
The js_watch_set fake-up code:

634                     memset(&frame, 0, sizeof(frame));
635                     frame.script = script;
636                     frame.regs = NULL;
637                     frame.fun = fun;
638                     frame.argv = argv + 2;
639                     frame.down = js_GetTopStackFrame(cx);
640                     frame.scopeChain = OBJ_GET_PARENT(cx, closure);
641                     if (script && script->nslots)
642                         frame.slots = argv + slotsStart;
643                     if (script) {
644                         JS_ASSERT(script->length >= JSOP_STOP_LENGTH);
645                         regs.pc = script->code + script->length
646                                   - JSOP_STOP_LENGTH;
647                         regs.sp = NULL;
648                         frame.regs = &regs;
649                         if (fun &&
650                             JSFUN_HEAVYWEIGHT_TEST(fun->flags) &&
651                             !js_GetCallObject(cx, &frame)) {
652                             if (argv != smallv)
653                                 cx->free(argv);
654                             DBG_LOCK(rt);
655                             DropWatchPointAndUnlock(cx, wp, JSWP_HELD);
656                             return JS_FALSE;
657                         }
658                     }
Attached patch So, fix?Splinter Review
The pseudo-frame is all about making the rest of the engine happy. This seems like the easiest way to do that.

That being said, there may be light at the end of the tunnel in bug 504021 which gives caps the ability to clamp a given context's principal. We'd need an API in the engine calling out to caps, but it would let us get rid of this pseudo frame which has been nothing but trouble since day one.
Assignee: general → mrbkap
Status: NEW → ASSIGNED
Attachment #400961 - Flags: review?(jwalden+bmo)
Comment on attachment 400961 [details] [diff] [review]
So, fix?

Sure, why not?
Attachment #400961 - Flags: review?(jwalden+bmo) → review+
../jsdbgapi.cpp: In function ‘JSBool js_watch_set(JSContext*, JSObject*, jsval, jsval*)’:
../jsdbgapi.cpp:647: error: ‘fp’ was not declared in this scope
make[1]: *** [jsdbgapi.o] Error 1
make: *** [default] Error 2
http://hg.mozilla.org/tracemonkey/rev/2f592bea272b with the obvious (and tested on my other computer fix to comment 7).
Whiteboard: fixed-in-tracemonkey
Target Milestone: --- → mozilla1.9.3a1
Depends on: 517041
bug 517041 is why not. bug 516911 is the real solution.
Flags: in-testsuite?
http://hg.mozilla.org/mozilla-central/rev/2f592bea272b
Status: ASSIGNED → RESOLVED
Closed: 15 years ago
Resolution: --- → FIXED
Is this something we need to worry about on older branches, or was it introduced recently? Some of the regressions from this were marked blocking1.9.2+, does it go back further than that?
Crash Signature: [@ GetGCThingFlags] [@ JS_CallTracer]
Group: core-security
Automatically extracted testcase for this bug was committed:

https://hg.mozilla.org/mozilla-central/rev/efaf8960a929
Flags: in-testsuite? → in-testsuite+
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: