Bug 700822: Randomize JIT VirtualAlloc location on win32, sans XP. (r=dmandelin)
authorChris Leary <cdleary@mozilla.com>
Mon, 13 Feb 2012 21:36:11 -0800
changeset 86827 9e93f190f64cda2271479b07c21b6d844b3f432d
parent 86826 85f3cf72938ae7e8e49fd3480cc3b7c51d25b11b
child 86828 e20072b2802ceace5a29bd3cf1f8c941dbd74045
push id6058
push usercleary@mozilla.com
push dateTue, 14 Feb 2012 19:43:54 +0000
treeherdermozilla-inbound@9e93f190f64c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdmandelin
bugs700822
milestone13.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 700822: Randomize JIT VirtualAlloc location on win32, sans XP. (r=dmandelin)
dom/base/nsJSEnvironment.cpp
dom/workers/RuntimeService.cpp
js/src/assembler/jit/ExecutableAllocator.cpp
js/src/assembler/jit/ExecutableAllocator.h
js/src/assembler/jit/ExecutableAllocatorWin.cpp
js/src/jsapi-tests/valueABI.c
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jsmath.cpp
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/MethodJIT.h
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -991,21 +991,16 @@ nsJSContext::JSOptionChangedCallback(con
   else
     newDefaultJSOptions &= ~JSOPTION_PCCOUNT;
 
   if (useMethodJITAlways)
     newDefaultJSOptions |= JSOPTION_METHODJIT_ALWAYS;
   else
     newDefaultJSOptions &= ~JSOPTION_METHODJIT_ALWAYS;
 
-  if (useHardening)
-    newDefaultJSOptions &= ~JSOPTION_SOFTEN;
-  else
-    newDefaultJSOptions |= JSOPTION_SOFTEN;
-
   if (useTypeInference)
     newDefaultJSOptions |= JSOPTION_TYPE_INFERENCE;
   else
     newDefaultJSOptions &= ~JSOPTION_TYPE_INFERENCE;
 
 #ifdef DEBUG
   // In debug builds, warnings are enabled in chrome context if
   // javascript.options.strict.debug is true
@@ -1030,16 +1025,19 @@ nsJSContext::JSOptionChangedCallback(con
   else
     newDefaultJSOptions &= ~JSOPTION_RELIMIT;
 
   ::JS_SetOptions(context->mContext, newDefaultJSOptions & JSRUNOPTION_MASK);
 
   // Save the new defaults for the next page load (InitContext).
   context->mDefaultJSOptions = newDefaultJSOptions;
 
+  JSRuntime *rt = JS_GetRuntime(context->mContext);
+  JS_SetJitHardening(rt, useHardening);
+
 #ifdef JS_GC_ZEAL
   PRInt32 zeal = Preferences::GetInt(js_zeal_option_str, -1);
   PRInt32 frequency = Preferences::GetInt(js_zeal_frequency_str, JS_DEFAULT_ZEAL_FREQ);
   bool compartment = Preferences::GetBool(js_zeal_compartment_str, false);
   if (zeal >= 0)
     ::JS_SetGCZeal(context->mContext, (PRUint8)zeal, frequency, compartment);
 #endif
 
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -238,21 +238,16 @@ PrefCallback(const char* aPrefName, void
     }
     if (Preferences::GetBool(gPrefsToWatch[PREF_methodjit_always])) {
       newOptions |= JSOPTION_METHODJIT_ALWAYS;
     }
     if (Preferences::GetBool(gPrefsToWatch[PREF_typeinference])) {
       newOptions |= JSOPTION_TYPE_INFERENCE;
     }
 
-    // This one is special, it's enabled by default and only needs to be unset.
-    if (!Preferences::GetBool(gPrefsToWatch[PREF_jit_hardening])) {
-      newOptions |= JSOPTION_SOFTEN;
-    }
-
     RuntimeService::SetDefaultJSContextOptions(newOptions);
     rts->UpdateAllWorkerJSContextOptions();
   }
 #ifdef JS_GC_ZEAL
   else if (!strcmp(aPrefName, gPrefsToWatch[PREF_gczeal])) {
     PRInt32 gczeal = Preferences::GetInt(gPrefsToWatch[PREF_gczeal]);
     RuntimeService::SetDefaultGCZeal(PRUint8(clamped(gczeal, 0, 3)));
     rts->UpdateAllWorkerGCZeal();
--- a/js/src/assembler/jit/ExecutableAllocator.cpp
+++ b/js/src/assembler/jit/ExecutableAllocator.cpp
@@ -22,16 +22,18 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
 #include "ExecutableAllocator.h"
 
 #if ENABLE_ASSEMBLER
 
+#include "prmjtime.h"
+
 namespace JSC {
 
 size_t ExecutableAllocator::pageSize = 0;
 size_t ExecutableAllocator::largeAllocSize = 0;
 
 ExecutablePool::~ExecutablePool()
 {
     m_allocator->releasePoolPages(this);
--- a/js/src/assembler/jit/ExecutableAllocator.h
+++ b/js/src/assembler/jit/ExecutableAllocator.h
@@ -160,37 +160,51 @@ private:
     }
     
     size_t available() const { 
         JS_ASSERT(m_end >= m_freePtr);
         return m_end - m_freePtr;
     }
 };
 
+enum AllocationBehavior
+{
+    AllocationCanRandomize,
+    AllocationDeterministic
+};
+
 class ExecutableAllocator {
     typedef void (*DestroyCallback)(void* addr, size_t size);
     enum ProtectionSetting { Writable, Executable };
     DestroyCallback destroyCallback;
 
+    void initSeed();
+
 public:
-    ExecutableAllocator() : destroyCallback(NULL)
+    explicit ExecutableAllocator(AllocationBehavior allocBehavior)
+      : destroyCallback(NULL),
+        allocBehavior(allocBehavior)
     {
         if (!pageSize) {
             pageSize = determinePageSize();
             /*
              * On Windows, VirtualAlloc effectively allocates in 64K chunks.
              * (Technically, it allocates in page chunks, but the starting
              * address is always a multiple of 64K, so each allocation uses up
              * 64K of address space.)  So a size less than that would be
              * pointless.  But it turns out that 64KB is a reasonable size for
              * all platforms.  (This assumes 4KB pages.)
              */
             largeAllocSize = pageSize * 16;
         }
 
+#if WTF_OS_WINDOWS
+        initSeed();
+#endif
+
         JS_ASSERT(m_smallPools.empty());
     }
 
     ~ExecutableAllocator()
     {
         for (size_t i = 0; i < m_smallPools.length(); i++)
             m_smallPools[i]->release(/* willDestroy = */true);
         // XXX: temporarily disabled because it fails;  see bug 654820.
@@ -231,19 +245,26 @@ public:
     }
 
     void sizeOfCode(size_t *method, size_t *regexp, size_t *unused) const;
 
     void setDestroyCallback(DestroyCallback destroyCallback) {
         this->destroyCallback = destroyCallback;
     }
 
+    void setRandomize(bool enabled) {
+        allocBehavior = enabled ? AllocationCanRandomize : AllocationDeterministic;
+    }
+
 private:
     static size_t pageSize;
     static size_t largeAllocSize;
+#if WTF_OS_WINDOWS
+    static int64_t rngSeed;
+#endif
 
     static const size_t OVERSIZE_ALLOCATION = size_t(-1);
 
     static size_t roundUpAllocationSize(size_t request, size_t granularity)
     {
         // Something included via windows.h defines a macro with this name,
         // which causes the function below to fail to compile.
         #ifdef _MSC_VER
@@ -256,18 +277,19 @@ private:
         // Round up to next page boundary
         size_t size = request + (granularity - 1);
         size = size & ~(granularity - 1);
         JS_ASSERT(size >= request);
         return size;
     }
 
     // On OOM, this will return an Allocation where pages is NULL.
-    static ExecutablePool::Allocation systemAlloc(size_t n);
+    ExecutablePool::Allocation systemAlloc(size_t n);
     static void systemRelease(const ExecutablePool::Allocation& alloc);
+    void *computeRandomAllocationAddress();
 
     ExecutablePool* createPool(size_t n)
     {
         size_t allocSize = roundUpAllocationSize(n, pageSize);
         if (allocSize == OVERSIZE_ALLOCATION)
             return NULL;
 
         if (!m_pools.initialized() && !m_pools.init())
@@ -461,16 +483,17 @@ private:
     SmallExecPoolVector m_smallPools;
 
     // All live pools are recorded here, just for stats purposes.  These are
     // weak references;  they don't keep pools alive.  When a pool is destroyed
     // its reference is removed from m_pools.
     typedef js::HashSet<ExecutablePool *, js::DefaultHasher<ExecutablePool *>, js::SystemAllocPolicy>
             ExecPoolHashSet;
     ExecPoolHashSet m_pools;    // All pools, just for stats purposes.
+    AllocationBehavior allocBehavior;
 
     static size_t determinePageSize();
 };
 
 }
 
 #endif // ENABLE(ASSEMBLER)
 
--- a/js/src/assembler/jit/ExecutableAllocatorWin.cpp
+++ b/js/src/assembler/jit/ExecutableAllocatorWin.cpp
@@ -15,45 +15,111 @@
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-
 #include "ExecutableAllocator.h"
 
 #if ENABLE_ASSEMBLER && WTF_OS_WINDOWS
 
 #include "jswin.h"
+#include "prmjtime.h"
+
+extern void random_setSeed(int64_t *, int64_t);
+extern uint64_t random_next(int64_t *, int);
 
 namespace JSC {
 
+int64_t ExecutableAllocator::rngSeed;
+
+void ExecutableAllocator::initSeed()
+{
+    random_setSeed(&rngSeed, (PRMJ_Now() / 1000) ^ int64_t(this));
+}
+
 size_t ExecutableAllocator::determinePageSize()
 {
     SYSTEM_INFO system_info;
     GetSystemInfo(&system_info);
     return system_info.dwPageSize;
 }
 
+void *ExecutableAllocator::computeRandomAllocationAddress()
+{
+    /*
+     * Inspiration is V8's OS::Allocate in platform-win32.cc.
+     *
+     * VirtualAlloc takes 64K chunks out of the virtual address space, so we
+     * keep 16b alignment.
+     *
+     * x86: V8 comments say that keeping addresses in the [64MiB, 1GiB) range
+     * tries to avoid system default DLL mapping space. In the end, we get 13
+     * bits of randomness in our selection.
+     * x64: [2GiB, 4TiB), with 25 bits of randomness.
+     */
+    static const uintN chunkBits = 16;
+#if WTF_CPU_X86_64
+    static const uintptr_t base = 0x0000000080000000;
+    static const uintptr_t mask = 0x000003ffffff0000;
+#elif WTF_CPU_X86
+    static const uintptr_t base = 0x04000000;
+    static const uintptr_t mask = 0x3fff0000;
+#else
+# error "Unsupported architecture"
+#endif
+    uint64_t rand = random_next(&rngSeed, 32) << chunkBits;
+    return (void *) (base | rand & mask);
+}
+
+static bool
+RandomizeIsBrokenImpl()
+{
+    OSVERSIONINFO osvi;
+    ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+
+    GetVersionEx(&osvi);
+
+    // Version number mapping is available at:
+    // http://msdn.microsoft.com/en-us/library/ms724832%28v=vs.85%29.aspx
+    // We disable everything before Vista, for now.
+    return osvi.dwMajorVersion <= 5;
+}
+
+static bool
+RandomizeIsBroken()
+{
+    static int result = RandomizeIsBrokenImpl();
+    return !!result;
+}
+
 ExecutablePool::Allocation ExecutableAllocator::systemAlloc(size_t n)
 {
-    void *allocation = VirtualAlloc(0, n, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+    void *allocation = NULL;
+    if (allocBehavior == AllocationCanRandomize && !RandomizeIsBroken()) {
+        void *randomAddress = computeRandomAllocationAddress();
+        allocation = VirtualAlloc(randomAddress, n, MEM_COMMIT | MEM_RESERVE,
+                                  PAGE_EXECUTE_READWRITE);
+    }
+    if (!allocation)
+        allocation = VirtualAlloc(0, n, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
     ExecutablePool::Allocation alloc = { reinterpret_cast<char*>(allocation), n };
     return alloc;
 }
 
 void ExecutableAllocator::systemRelease(const ExecutablePool::Allocation& alloc)
-{ 
-    VirtualFree(alloc.pages, 0, MEM_RELEASE); 
+{
+    VirtualFree(alloc.pages, 0, MEM_RELEASE);
 }
 
 #if ENABLE_ASSEMBLER_WX_EXCLUSIVE
 #error "ASSEMBLER_WX_EXCLUSIVE not yet suported on this platform."
 #endif
 
 }
 
--- a/js/src/jsapi-tests/valueABI.c
+++ b/js/src/jsapi-tests/valueABI.c
@@ -1,11 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=99 ft=c:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla SpiderMonkey JavaScript code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
 #include "jsapi.h"
 
-// See testValueABI.cpp
+/* See testValueABI.cpp */
 
 JSBool
 C_ValueToObject(JSContext *cx, jsval v, JSObject **obj)
 {
     return JS_ValueToObject(cx, v, obj);
 }
 
 jsval
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1323,16 +1323,22 @@ JS_PUBLIC_API(uint32_t)
 JS_ToggleOptions(JSContext *cx, uint32_t options)
 {
     AutoLockGC lock(cx->runtime);
     uintN oldopts = cx->allOptions();
     uintN newopts = oldopts ^ options;
     return SetOptionsCommon(cx, newopts);
 }
 
+JS_PUBLIC_API(void)
+JS_SetJitHardening(JSRuntime *rt, JSBool enabled)
+{
+    rt->setJitHardening(!!enabled);
+}
+
 JS_PUBLIC_API(const char *)
 JS_GetImplementationVersion(void)
 {
     return "JavaScript-C 1.8.5+ 2011-04-16";
 }
 
 JS_PUBLIC_API(JSCompartmentCallback)
 JS_SetCompartmentCallback(JSRuntime *rt, JSCompartmentCallback callback)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2566,33 +2566,35 @@ JS_StringToVersion(const char *string);
 /* JS_BIT(15) is currently unused. */
 
 #define JSOPTION_METHODJIT_ALWAYS \
                                 JS_BIT(16)      /* Always whole-method JIT,
                                                    don't tune at run-time. */
 #define JSOPTION_PCCOUNT        JS_BIT(17)      /* Collect per-op execution counts */
 
 #define JSOPTION_TYPE_INFERENCE JS_BIT(18)      /* Perform type inference. */
-#define JSOPTION_SOFTEN         JS_BIT(19)      /* Disable JIT hardening. */
 
 /* Options which reflect compile-time properties of scripts. */
 #define JSCOMPILEOPTION_MASK    (JSOPTION_XML)
 
-#define JSRUNOPTION_MASK        (JS_BITMASK(20) & ~JSCOMPILEOPTION_MASK)
+#define JSRUNOPTION_MASK        (JS_BITMASK(19) & ~JSCOMPILEOPTION_MASK)
 #define JSALLOPTION_MASK        (JSCOMPILEOPTION_MASK | JSRUNOPTION_MASK)
 
 extern JS_PUBLIC_API(uint32_t)
 JS_GetOptions(JSContext *cx);
 
 extern JS_PUBLIC_API(uint32_t)
 JS_SetOptions(JSContext *cx, uint32_t options);
 
 extern JS_PUBLIC_API(uint32_t)
 JS_ToggleOptions(JSContext *cx, uint32_t options);
 
+extern JS_PUBLIC_API(void)
+JS_SetJitHardening(JSRuntime *rt, JSBool enabled);
+
 extern JS_PUBLIC_API(const char *)
 JS_GetImplementationVersion(void);
 
 extern JS_PUBLIC_API(JSCompartmentCallback)
 JS_SetCompartmentCallback(JSRuntime *rt, JSCompartmentCallback callback);
 
 extern JS_PUBLIC_API(JSWrapObjectCallback)
 JS_SetWrapObjectCallbacks(JSRuntime *rt,
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -74,16 +74,17 @@
 #include "jsopcode.h"
 #include "jspubtd.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstr.h"
 
 #ifdef JS_METHODJIT
 # include "assembler/assembler/MacroAssembler.h"
+# include "methodjit/MethodJIT.h"
 #endif
 #include "frontend/TokenStream.h"
 #include "frontend/ParseMaps.h"
 #include "yarr/BumpPointerAllocator.h"
 
 #include "jsatominlines.h"
 #include "jscntxtinlines.h"
 #include "jscompartment.h"
@@ -119,23 +120,33 @@ JSRuntime::triggerOperationCallback()
 {
     /*
      * Use JS_ATOMIC_SET in the hope that it ensures the write will become
      * immediately visible to other processors polling the flag.
      */
     JS_ATOMIC_SET(&interrupt, 1);
 }
 
+void
+JSRuntime::setJitHardening(bool enabled)
+{
+    jitHardening = enabled;
+    if (execAlloc_)
+        execAlloc_->setRandomize(enabled);
+}
+
 JSC::ExecutableAllocator *
 JSRuntime::createExecutableAllocator(JSContext *cx)
 {
     JS_ASSERT(!execAlloc_);
     JS_ASSERT(cx->runtime == this);
 
-    execAlloc_ = new_<JSC::ExecutableAllocator>();
+    JSC::AllocationBehavior randomize =
+        jitHardening ? JSC::AllocationCanRandomize : JSC::AllocationDeterministic;
+    execAlloc_ = new_<JSC::ExecutableAllocator>(randomize);
     if (!execAlloc_)
         js_ReportOutOfMemory(cx);
     return execAlloc_;
 }
 
 WTF::BumpPointerAllocator *
 JSRuntime::createBumpPointerAllocator(JSContext *cx)
 {
@@ -1236,16 +1247,19 @@ IsJITBrokenHere()
     return isBroken;
 }
 #endif
 
 void
 JSContext::updateJITEnabled()
 {
 #ifdef JS_METHODJIT
+    // This allocator randomization is actually a compartment-wide option.
+    if (compartment && compartment->hasJaegerCompartment())
+        compartment->jaegerCompartment()->execAlloc()->setRandomize(runtime->getJitHardening());
     methodJitEnabled = (runOptions & JSOPTION_METHODJIT) && !IsJITBrokenHere();
 #endif
 }
 
 size_t
 JSContext::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const
 {
     /*
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -526,16 +526,18 @@ struct JSRuntime : js::RuntimeFriendFiel
 
     /*
      * To ensure that cx->malloc does not cause a GC, we set this flag during
      * OOM reporting (in js_ReportOutOfMemory). If a GC is requested while
      * reporting the OOM, we ignore it.
      */
     int32_t             inOOMReport;
 
+    bool                jitHardening;
+
     JSRuntime();
     ~JSRuntime();
 
     bool init(uint32_t maxbytes);
 
     JSRuntime *thisFromCtor() { return this; }
 
     /*
@@ -619,16 +621,21 @@ struct JSRuntime : js::RuntimeFriendFiel
      * Other values of p mean a realloc failure.
      *
      * The function must be called outside the GC lock.
      */
     JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes, JSContext *cx);
 
     JS_FRIEND_API(void) triggerOperationCallback();
 
+    void setJitHardening(bool enabled);
+    bool getJitHardening() const {
+        return jitHardening;
+    }
+
     void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
                              size_t *regexpCode, size_t *stackCommitted);
 
     void purge(JSContext *cx);
 };
 
 /* Common macros to access thread-local caches in JSRuntime. */
 #define JS_PROPERTY_CACHE(cx)   (cx->runtime->propertyCache)
@@ -910,17 +917,16 @@ struct JSContext : js::ContextFriendFiel
     bool hasRunOption(uintN ropt) const {
         JS_ASSERT((ropt & JSRUNOPTION_MASK) == ropt);
         return !!(runOptions & ropt);
     }
 
     bool hasStrictOption() const { return hasRunOption(JSOPTION_STRICT); }
     bool hasWErrorOption() const { return hasRunOption(JSOPTION_WERROR); }
     bool hasAtLineOption() const { return hasRunOption(JSOPTION_ATLINE); }
-    bool hasJITHardeningOption() const { return !hasRunOption(JSOPTION_SOFTEN); }
 
     js::LifoAlloc &tempLifoAlloc() { return runtime->tempLifoAlloc; }
     inline js::LifoAlloc &typeLifoAlloc();
 
 #ifdef JS_THREADSAFE
     unsigned            outstandingRequests;/* number of JS_BeginRequest calls
                                                without the corresponding
                                                JS_EndRequest. */
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -136,17 +136,17 @@ bool
 JSCompartment::ensureJaegerCompartmentExists(JSContext *cx)
 {
     if (jaegerCompartment_)
         return true;
 
     mjit::JaegerCompartment *jc = cx->new_<mjit::JaegerCompartment>();
     if (!jc)
         return false;
-    if (!jc->Initialize()) {
+    if (!jc->Initialize(cx)) {
         cx->delete_(jc);
         return false;
     }
     jaegerCompartment_ = jc;
     return true;
 }
 
 size_t
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -511,51 +511,49 @@ js_math_pow(JSContext *cx, uintN argc, V
 static const int64_t RNG_MULTIPLIER = 0x5DEECE66DLL;
 static const int64_t RNG_ADDEND = 0xBLL;
 static const int64_t RNG_MASK = (1LL << 48) - 1;
 static const jsdouble RNG_DSCALE = jsdouble(1LL << 53);
 
 /*
  * Math.random() support, lifted from java.util.Random.java.
  */
-static inline void
-random_setSeed(JSContext *cx, int64_t seed)
+extern void
+random_setSeed(int64_t *rngSeed, int64_t seed)
 {
-    cx->rngSeed = (seed ^ RNG_MULTIPLIER) & RNG_MASK;
+    *rngSeed = (seed ^ RNG_MULTIPLIER) & RNG_MASK;
 }
 
 void
 js_InitRandom(JSContext *cx)
 {
     /*
      * Set the seed from current time. Since we have a RNG per context and we often bring
      * up several contexts at the same time, we xor in some additional values, namely
      * the context and its successor. We don't just use the context because it might be
      * possible to reverse engineer the context pointer if one guesses the time right.
      */
-    random_setSeed(cx,
-                   (PRMJ_Now() / 1000) ^
-                   int64_t(cx) ^
-                   int64_t(cx->link.next));
+    random_setSeed(&cx->rngSeed, (PRMJ_Now() / 1000) ^ int64_t(cx) ^ int64_t(cx->link.next));
 }
 
-static inline uint64_t
-random_next(JSContext *cx, int bits)
+extern uint64_t
+random_next(int64_t *rngSeed, int bits)
 {
-    uint64_t nextseed = cx->rngSeed * RNG_MULTIPLIER;
+    uint64_t nextseed = *rngSeed * RNG_MULTIPLIER;
     nextseed += RNG_ADDEND;
     nextseed &= RNG_MASK;
-    cx->rngSeed = nextseed;
+    *rngSeed = nextseed;
     return nextseed >> (48 - bits);
 }
 
 static inline jsdouble
 random_nextDouble(JSContext *cx)
 {
-    return jsdouble((random_next(cx, 26) << 27) + random_next(cx, 27)) / RNG_DSCALE;
+    return jsdouble((random_next(&cx->rngSeed, 26) << 27) + random_next(&cx->rngSeed, 27)) /
+           RNG_DSCALE;
 }
 
 static JSBool
 math_random(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble z = random_nextDouble(cx);
     vp->setDouble(z);
     return JS_TRUE;
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -984,19 +984,20 @@ JS_STATIC_ASSERT(JSVAL_PAYLOAD_MASK == 0
 
 #endif                   /* _WIN64 */
 
 JaegerCompartment::JaegerCompartment()
     : orphanedNativeFrames(SystemAllocPolicy()), orphanedNativePools(SystemAllocPolicy())
 {}
 
 bool
-JaegerCompartment::Initialize()
+JaegerCompartment::Initialize(JSContext *cx)
 {
-    execAlloc_ = js::OffTheBooks::new_<JSC::ExecutableAllocator>();
+    execAlloc_ = js::OffTheBooks::new_<JSC::ExecutableAllocator>(
+        cx->runtime->getJitHardening() ? JSC::AllocationCanRandomize : JSC::AllocationDeterministic);
     if (!execAlloc_)
         return false;
     
     TrampolineCompiler tc(execAlloc_, &trampolines);
     if (!tc.compile()) {
         js::Foreground::delete_(execAlloc_);
         execAlloc_ = NULL;
         return false;
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -468,17 +468,17 @@ class JaegerCompartment {
     Trampolines              trampolines;    // force-return trampolines
     VMFrame                  *activeFrame_;  // current active VMFrame
     JaegerStatus             lastUnfinished_;// result status of last VM frame,
                                              // if unfinished
 
     void Finish();
 
   public:
-    bool Initialize();
+    bool Initialize(JSContext *cx);
 
     JaegerCompartment();
     ~JaegerCompartment() { Finish(); }
 
     JSC::ExecutableAllocator *execAlloc() {
         return execAlloc_;
     }