Bug 649939: Prevent crash from too much recursion in Iterator.prototype.next() (r=jwalden)
authorPaul Biggar <pbiggar@mozilla.com>
Thu, 16 Jun 2011 17:00:31 -0700
changeset 71398 1f11cd362858310ea1fe1635f042744406480886
parent 71397 aa383564c5b6c97f3205d4d279263794e467da88
child 71399 8c1ddbd412979e5616fcf3c30bfdd63929adf190
push idunknown
push userunknown
push dateunknown
reviewersjwalden
bugs649939
milestone7.0a1
Bug 649939: Prevent crash from too much recursion in Iterator.prototype.next() (r=jwalden)
js/src/jit-test/tests/basic/bug649939.js
js/src/jscntxt.cpp
js/src/jsinterp.cpp
js/src/jsiter.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug649939.js
@@ -0,0 +1,27 @@
+// This was the actual bug
+assertRaises(StopIteration, function() {
+    Iterator.prototype.next();
+    Iterator.prototype.next();
+});
+
+// The error should have triggered here, but was masked by a latent bug
+assertRaises(StopIteration, function() {
+    Iterator.prototype.next();
+});
+
+// Found by fuzzing
+assertRaises(StopIteration, function() {
+    (new Iterator({})).__proto__.next();
+});
+
+
+function assertRaises(exc, callback) {
+    var caught = false;
+    try {
+        callback();
+    } catch (e) {
+        assertEq(e instanceof InternalError, true);
+        caught = true;
+    }
+    assertEq(caught, true);
+}
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -313,16 +313,17 @@ js_NewContext(JSRuntime *rt, size_t stac
     if (!mem)
         return NULL;
 
     cx = new (mem) JSContext(rt);
     cx->debugHooks = &rt->globalDebugHooks;
 #if JS_STACK_GROWTH_DIRECTION > 0
     cx->stackLimit = (jsuword) -1;
 #endif
+    cx->iterValue.setMagic(JS_NO_ITER_VALUE);
     JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0);
     JS_ASSERT(cx->findVersion() == JSVERSION_DEFAULT);
     VOUCH_DOES_NOT_REQUIRE_STACK();
 
     JS_InitArenaPool(&cx->tempPool, "temp", TEMP_POOL_CHUNK_SIZE, sizeof(jsdouble));
     JS_InitArenaPool(&cx->regExpPool, "regExp", TEMP_POOL_CHUNK_SIZE, sizeof(int));
 
     JS_ASSERT(cx->resolveFlags == 0);
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2071,33 +2071,33 @@ JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == 
 /*
  * Inline fast paths for iteration. js_IteratorMore and js_IteratorNext handle
  * all cases, but we inline the most frequently taken paths here.
  */
 static inline bool
 IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, Value *rval)
 {
     if (iterobj->getClass() == &js_IteratorClass) {
-        NativeIterator *ni = (NativeIterator *) iterobj->getPrivate();
+        NativeIterator *ni = iterobj->getNativeIterator();
         if (ni->isKeyIter()) {
             *cond = (ni->props_cursor < ni->props_end);
             return true;
         }
     }
     if (!js_IteratorMore(cx, iterobj, rval))
         return false;
     *cond = rval->isTrue();
     return true;
 }
 
 static inline bool
 IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
 {
     if (iterobj->getClass() == &js_IteratorClass) {
-        NativeIterator *ni = (NativeIterator *) iterobj->getPrivate();
+        NativeIterator *ni = iterobj->getNativeIterator();
         if (ni->isKeyIter()) {
             JS_ASSERT(ni->props_cursor < ni->props_end);
             jsid id = *ni->current();
             if (JSID_IS_ATOM(id)) {
                 rval->setString(JSID_TO_STRING(id));
                 ni->incCursor();
                 return true;
             }
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -937,16 +937,19 @@ js_IteratorMore(JSContext *cx, JSObject 
     }
 
     /* We might still have a pending value. */
     if (!cx->iterValue.isMagic(JS_NO_ITER_VALUE)) {
         rval->setBoolean(true);
         return true;
     }
 
+    /* We're reentering below and can call anything. */
+    JS_CHECK_RECURSION(cx, return false);
+
     /* Fetch and cache the next value from the iterator. */
     if (!ni) {
         jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom);
         if (!js_GetMethod(cx, iterobj, id, JSGET_METHOD_BARRIER, rval))
             return false;
         if (!ExternalInvoke(cx, ObjectValue(*iterobj), *rval, 0, NULL, rval)) {
             /* Check for StopIteration. */
             if (!cx->isExceptionPending() || !js_ValueIsStopIteration(cx->getPendingException()))