Bug 1282986 - Disallow creation of workers while running simulated OOM tests r=terrence
authorJon Coppeard <jcoppeard@mozilla.com>
Fri, 01 Jul 2016 09:37:37 +0100
changeset 383189 d88611ee1de75ee2cad24b902eafe074cc78c202
parent 383188 bdd62c877b66dcfa83034d276f0b0c452dfcaebe
child 383190 3f85f05c1e20bf03fe9a7bc6380301d332d7e460
push id21963
push userdmitchell@mozilla.com
push dateFri, 01 Jul 2016 19:54:18 +0000
reviewersterrence
bugs1282986
milestone50.0a1
Bug 1282986 - Disallow creation of workers while running simulated OOM tests r=terrence
js/src/builtin/TestingFunctions.cpp
js/src/jit-test/tests/gc/bug-1282986.js
js/src/shell/js.cpp
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1276,19 +1276,16 @@ OOMTest(JSContext* cx, unsigned argc, Va
     if (fuzzingSafe)
         expectExceptionOnFailure = false;
 
     if (disableOOMFunctions) {
         args.rval().setUndefined();
         return true;
     }
 
-    MOZ_ASSERT(!cx->isExceptionPending());
-    cx->runtime()->hadOutOfMemory = false;
-
     RootedFunction function(cx, &args[0].toObject().as<JSFunction>());
 
     bool verbose = EnvVarIsDefined("OOM_VERBOSE");
 
     unsigned threadStart = oom::THREAD_TYPE_MAIN;
     unsigned threadEnd = oom::THREAD_TYPE_MAX;
 
     // Test a single thread type if specified by the OOM_THREAD environment variable.
@@ -1298,16 +1295,26 @@ OOMTest(JSContext* cx, unsigned argc, Va
             JS_ReportError(cx, "OOM_THREAD value out of range.");
             return false;
         }
 
         threadStart = threadOption;
         threadEnd = threadOption + 1;
     }
 
+    JSRuntime* rt = cx->runtime();
+    if (rt->runningOOMTest) {
+        JS_ReportError(cx, "Nested call to oomTest() is not allowed.");
+        return false;
+    }
+    rt->runningOOMTest = true;
+
+    MOZ_ASSERT(!cx->isExceptionPending());
+    rt->hadOutOfMemory = false;
+
     JS_SetGCZeal(cx->runtime(), 0, JS_DEFAULT_ZEAL_FREQ);
 
     for (unsigned thread = threadStart; thread < threadEnd; thread++) {
         if (verbose)
             fprintf(stderr, "thread %d\n", thread);
 
         HelperThreadState().waitForAllThreads();
         js::oom::targetThread = thread;
@@ -1353,16 +1360,17 @@ OOMTest(JSContext* cx, unsigned argc, Va
             allocation++;
         } while (handledOOM);
 
         if (verbose) {
             fprintf(stderr, "  finished after %d allocations\n", allocation - 2);
         }
     }
 
+    rt->runningOOMTest = false;
     args.rval().setUndefined();
     return true;
 }
 #endif
 
 #ifdef SPIDERMONKEY_PROMISE
 static bool
 SettlePromiseNow(JSContext* cx, unsigned argc, Value* vp)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1282986.js
@@ -0,0 +1,17 @@
+if (!('oomTest' in this))
+    quit();
+
+var lfLogBuffer = `
+evalInWorker(\`
+        try { oomAfterAllocations(2); } catch(e) {}
+    \`);
+`;
+loadFile("");
+loadFile(lfLogBuffer);
+function loadFile(lfVarx) {
+    oomTest(function() {
+        let m = parseModule(lfVarx);
+        m.declarationInstantiation();
+        m.evaluation();
+    });
+}
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3021,16 +3021,23 @@ EvalInWorker(JSContext* cx, unsigned arg
     }
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.get(0).isString()) {
         JS_ReportError(cx, "Invalid arguments to evalInWorker");
         return false;
     }
 
+#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
+    if (cx->runtime()->runningOOMTest) {
+        JS_ReportError(cx, "Can't create workers while running simulated OOM test");
+        return false;
+    }
+#endif
+
     if (!args[0].toString()->ensureLinear(cx))
         return false;
 
     if (!workerThreadsLock) {
         workerThreadsLock = js_new<Mutex>();
         if (!workerThreadsLock) {
             ReportOutOfMemory(cx);
             return false;
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -199,16 +199,19 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     emptyString(nullptr),
     spsProfiler(thisFromCtor()),
     profilingScripts(false),
     suppressProfilerSampling(false),
     hadOutOfMemory(false),
 #ifdef DEBUG
     handlingInitFailure(false),
 #endif
+#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
+    runningOOMTest(false),
+#endif
     allowRelazificationForTesting(false),
     data(nullptr),
     signalHandlersInstalled_(false),
     canUseSignalHandlers_(false),
     defaultFreeOp_(thisFromCtor()),
     debuggerMutations(0),
     securityCallbacks(&NullSecurityCallbacks),
     DOMcallbacks(nullptr),
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1201,16 +1201,21 @@ struct JSRuntime : public JS::shadow::Ru
     /* Had an out-of-memory error which did not populate an exception. */
     bool                hadOutOfMemory;
 
 #ifdef DEBUG
     /* We are currently deleting an object due to an initialization failure. */
     bool handlingInitFailure;
 #endif
 
+#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
+    /* We are currently running a simulated OOM test. */
+    bool runningOOMTest;
+#endif
+
     /*
      * Allow relazifying functions in compartments that are active. This is
      * only used by the relazifyFunctions() testing function.
      */
     bool                allowRelazificationForTesting;
 
     /* Linked list of all Debugger objects in the runtime. */
     mozilla::LinkedList<js::Debugger> debuggerList;