Bug 753283 - Poison VM stack to help fuzzers (r=bhackett)
authorBill McCloskey <wmccloskey@mozilla.com>
Tue, 12 Jun 2012 12:24:31 -0700
changeset 96511 8d857c53bc0aebb3d8320da8aa9bcfe1c5027ea7
parent 96510 d514aa633475abf84ebae0be6b3ef0ecbd4a3014
child 96512 d7362d197229fadfe94f016139e731f12f4e7acb
push id22911
push useremorley@mozilla.com
push dateWed, 13 Jun 2012 12:49:30 +0000
treeherdermozilla-central@efbb6480e98e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs753283
milestone16.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 753283 - Poison VM stack to help fuzzers (r=bhackett)
js/src/jit-test/tests/basic/bug753283.js
js/src/jsapi.h
js/src/jsinterp.cpp
js/src/methodjit/Compiler.cpp
js/src/vm/Stack.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug753283.js
@@ -0,0 +1,26 @@
+var summary = '';
+function printStatus (msg) {
+  var lines = msg.split ("\n");
+}
+evaluate("\
+function f() {\
+    var ss = [\
+        new f(Int8Array, propertyIsEnumerable, '[let (x = 3, y = 4) x].map(0)')\
+    ];\
+}\
+try {\
+    f();\
+} catch (e) {}\
+  gczeal(4);\
+  printStatus (summary);\
+");
+evaluate("\
+function g(n, h) {\
+    var a = f;\
+    if (n <= 0) \
+    return f; \
+    var t = g(n - 1, h);\
+    var r = function(x) {    };\
+}\
+g(80, f);\
+");
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -668,16 +668,24 @@ static JS_ALWAYS_INLINE Value
 ObjectValue(JSObject &obj)
 {
     Value v;
     v.setObject(obj);
     return v;
 }
 
 static JS_ALWAYS_INLINE Value
+ObjectValueCrashOnTouch()
+{
+    Value v;
+    v.setObject(*reinterpret_cast<JSObject *>(0x42));
+    return v;
+}
+
+static JS_ALWAYS_INLINE Value
 MagicValue(JSWhyMagic why)
 {
     Value v;
     v.setMagic(why);
     return v;
 }
 
 static JS_ALWAYS_INLINE Value
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1054,21 +1054,21 @@ js::Interpret(JSContext *cx, StackFrame 
 
     register void * const *jumpTable = normalJumpTable;
 
     typedef GenericInterruptEnabler<void * const *> InterruptEnabler;
     InterruptEnabler interruptEnabler(&jumpTable, interruptJumpTable);
 
 # define DO_OP()            JS_BEGIN_MACRO                                    \
                                 CHECK_PCCOUNT_INTERRUPTS();                   \
-                                js::gc::MaybeVerifyBarriers(cx);              \
                                 JS_EXTENSION_(goto *jumpTable[op]);           \
                             JS_END_MACRO
 # define DO_NEXT_OP(n)      JS_BEGIN_MACRO                                    \
                                 TypeCheckNextBytecode(cx, script, n, regs);   \
+                                js::gc::MaybeVerifyBarriers(cx);              \
                                 op = (JSOp) *(regs.pc += (n));                \
                                 DO_OP();                                      \
                             JS_END_MACRO
 
 # define BEGIN_CASE(OP)     L_##OP:
 # define END_CASE(OP)       DO_NEXT_OP(OP##_LENGTH);
 # define END_VARLEN_CASE    DO_NEXT_OP(len);
 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)                                    \
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -1205,28 +1205,40 @@ mjit::Compiler::markUndefinedLocal(uint3
     uint32_t slot = LocalSlot(script, i);
     Address local(JSFrameReg, sizeof(StackFrame) + (depth + i) * sizeof(Value));
     if (!cx->typeInferenceEnabled() || !analysis->trackSlot(slot)) {
         masm.storeValue(UndefinedValue(), local);
     } else {
         Lifetime *lifetime = analysis->liveness(slot).live(offset);
         if (lifetime)
             masm.storeValue(UndefinedValue(), local);
+#ifdef DEBUG
+        else
+            masm.storeValue(ObjectValueCrashOnTouch(), local);
+#endif
     }
 }
 
 void
 mjit::Compiler::markUndefinedLocals()
 {
     /*
      * Set locals to undefined. Skip locals which aren't closed and are known
      * to be defined before used,
      */
     for (uint32_t i = 0; i < script->nfixed; i++)
         markUndefinedLocal(0, i);
+
+#ifdef DEBUG
+    uint32_t depth = ssa.getFrame(a->inlineIndex).depth;
+    for (uint32_t i = script->nfixed; i < script->nslots; i++) {
+        Address local(JSFrameReg, sizeof(StackFrame) + (depth + i) * sizeof(Value));
+        masm.storeValue(ObjectValueCrashOnTouch(), local);
+    }
+#endif
 }
 
 CompileStatus
 mjit::Compiler::generateEpilogue()
 {
     return Compile_Okay;
 }
 
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -508,31 +508,34 @@ StackSpace::init()
         return false;
     void *check = VirtualAlloc(p, COMMIT_BYTES, MEM_COMMIT, PAGE_READWRITE);
     if (p != check)
         return false;
     base_ = reinterpret_cast<Value *>(p);
     conservativeEnd_ = commitEnd_ = base_ + COMMIT_VALS;
     trustedEnd_ = base_ + CAPACITY_VALS;
     defaultEnd_ = trustedEnd_ - BUFFER_VALS;
+    Debug_SetValueRangeToCrashOnTouch(base_, commitEnd_);
 #elif defined(XP_OS2)
     if (DosAllocMem(&p, CAPACITY_BYTES, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_ANY) &&
         DosAllocMem(&p, CAPACITY_BYTES, PAG_COMMIT | PAG_READ | PAG_WRITE))
         return false;
     base_ = reinterpret_cast<Value *>(p);
     trustedEnd_ = base_ + CAPACITY_VALS;
     conservativeEnd_ = defaultEnd_ = trustedEnd_ - BUFFER_VALS;
+    Debug_SetValueRangeToCrashOnTouch(base_, trustedEnd_);
 #else
     JS_ASSERT(CAPACITY_BYTES % getpagesize() == 0);
     p = mmap(NULL, CAPACITY_BYTES, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     if (p == MAP_FAILED)
         return false;
     base_ = reinterpret_cast<Value *>(p);
     trustedEnd_ = base_ + CAPACITY_VALS;
     conservativeEnd_ = defaultEnd_ = trustedEnd_ - BUFFER_VALS;
+    Debug_SetValueRangeToCrashOnTouch(base_, trustedEnd_);
 #endif
     assertInvariants();
     return true;
 }
 
 StackSpace::~StackSpace()
 {
     assertInvariants();
@@ -703,16 +706,18 @@ StackSpace::ensureSpaceSlow(JSContext *c
         int32_t size = static_cast<int32_t>(newCommit - commitEnd_) * sizeof(Value);
 
         if (!VirtualAlloc(commitEnd_, size, MEM_COMMIT, PAGE_READWRITE)) {
             if (report)
                 js_ReportOverRecursed(cx);
             return false;
         }
 
+        Debug_SetValueRangeToCrashOnTouch(commitEnd_, newCommit);
+
         commitEnd_ = newCommit;
         conservativeEnd_ = Min(commitEnd_, defaultEnd_);
         assertInvariants();
     }
 #endif
 
     return true;
 }
@@ -881,19 +886,23 @@ ContextStack::pushInvokeArgs(JSContext *
 
 void
 ContextStack::popInvokeArgs(const InvokeArgsGuard &iag)
 {
     JS_ASSERT(iag.pushed());
     JS_ASSERT(onTop());
     JS_ASSERT(space().firstUnused() == seg_->calls().end());
 
+    Value *oldend = seg_->end();
+
     seg_->popCall();
     if (iag.pushedSeg_)
         popSegment();
+
+    Debug_SetValueRangeToCrashOnTouch(space().firstUnused(), oldend);
 }
 
 bool
 ContextStack::pushInvokeFrame(JSContext *cx, const CallArgs &args,
                               InitialFrameFlags initial, InvokeFrameGuard *ifg)
 {
     JS_ASSERT(onTop());
     JS_ASSERT(space().firstUnused() == args.end());
@@ -992,20 +1001,24 @@ ContextStack::pushDummyFrame(JSContext *
 void
 ContextStack::popFrame(const FrameGuard &fg)
 {
     JS_ASSERT(fg.pushed());
     JS_ASSERT(onTop());
     JS_ASSERT(space().firstUnused() == fg.regs_.sp);
     JS_ASSERT(&fg.regs_ == &seg_->regs());
 
+    Value *oldend = seg_->end();
+
     seg_->popRegs(fg.prevRegs_);
     if (fg.pushedSeg_)
         popSegment();
 
+    Debug_SetValueRangeToCrashOnTouch(space().firstUnused(), oldend);
+
     /*
      * NB: this code can call out and observe the stack (e.g., through GC), so
      * it should only be called from a consistent stack state.
      */
     if (!hasfp())
         cx_->resetCompartment();
 }