From ca9e2f24e64c07b40b813464ac017e0fcf6efa73 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Fri, 14 Dec 2012 14:27:22 -0600 Subject: [PATCH] Bug 820180 - Isolate JS pseudorandom number generator state per compartment. r=luke. --- js/src/assembler/jit/ExecutableAllocator.h | 2 +- .../assembler/jit/ExecutableAllocatorWin.cpp | 6 +-- js/src/jsapi.cpp | 3 +- js/src/jscntxt.cpp | 3 -- js/src/jscntxt.h | 13 +++++-- js/src/jscompartment.cpp | 4 ++ js/src/jscompartment.h | 3 ++ js/src/jsmath.cpp | 39 ++++++++++--------- js/src/jsmath.h | 6 +-- 9 files changed, 46 insertions(+), 33 deletions(-) diff --git a/js/src/assembler/jit/ExecutableAllocator.h b/js/src/assembler/jit/ExecutableAllocator.h index 2fe679b9a31..20015a255b8 100644 --- a/js/src/assembler/jit/ExecutableAllocator.h +++ b/js/src/assembler/jit/ExecutableAllocator.h @@ -267,7 +267,7 @@ private: static size_t pageSize; static size_t largeAllocSize; #if WTF_OS_WINDOWS - static int64_t rngSeed; + static uint64_t rngSeed; #endif static const size_t OVERSIZE_ALLOCATION = size_t(-1); diff --git a/js/src/assembler/jit/ExecutableAllocatorWin.cpp b/js/src/assembler/jit/ExecutableAllocatorWin.cpp index 910f44720b8..6fd93545ab8 100644 --- a/js/src/assembler/jit/ExecutableAllocatorWin.cpp +++ b/js/src/assembler/jit/ExecutableAllocatorWin.cpp @@ -30,12 +30,12 @@ #include "jswin.h" #include "prmjtime.h" -extern void random_setSeed(int64_t *, int64_t); -extern uint64_t random_next(int64_t *, int); +extern void random_setSeed(uint64_t *, uint64_t); +extern uint64_t random_next(uint64_t *, int); namespace JSC { -int64_t ExecutableAllocator::rngSeed; +uint64_t ExecutableAllocator::rngSeed; void ExecutableAllocator::initSeed() { diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 0474c402728..356db90ea5a 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -890,7 +890,8 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads) ctypesActivityCallback(NULL), ionReturnOverride_(MagicValue(JS_ARG_POISON)), useHelperThreads_(useHelperThreads), - requestedHelperThreadCount(-1) + requestedHelperThreadCount(-1), + rngNonce(0) { /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */ JS_INIT_CLIST(&onNewGlobalObjectWatchers); diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 01fc9229b49..c300c4ec5fc 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -265,8 +265,6 @@ js::NewContext(JSRuntime *rt, size_t stackChunkSize) bool first = rt->contextList.isEmpty(); rt->contextList.insertBack(cx); - js_InitRandom(cx); - /* * If cx is the first context on this runtime, initialize well-known atoms, * keywords, numbers, strings and self-hosted scripts. If one of these @@ -1117,7 +1115,6 @@ JSContext::JSContext(JSRuntime *rt) outstandingRequests(0), #endif resolveFlags(0), - rngSeed(0), iterValue(MagicValue(JS_NO_ITER_VALUE)), #ifdef JS_METHODJIT methodJitEnabled(false), diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 47982188b4c..e78324bf3d8 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1220,6 +1220,16 @@ struct JSRuntime : js::RuntimeFriendFields return 0; #endif } + + private: + /* + * Used to ensure that compartments created at the same time get different + * random number sequences. See js::InitRandom. + */ + uint64_t rngNonce; + + public: + uint64_t nextRNGNonce() { return rngNonce++; } }; /* Common macros to access thread-local caches in JSRuntime. */ @@ -1583,9 +1593,6 @@ struct JSContext : js::ContextFriendFields, /* Stored here to avoid passing it around as a parameter. */ unsigned resolveFlags; - /* Random number generator state, used by jsmath.cpp. */ - int64_t rngSeed; - /* Location to stash the iteration value between JSOP_MOREITER and JSOP_ITERNEXT. */ js::Value iterValue; diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 03c34df2b4d..756b11463e9 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -80,6 +80,7 @@ JSCompartment::JSCompartment(JSRuntime *rt) gcGrayRoots(), gcMallocBytes(0), debugModeBits(rt->debugMode ? DebugFromC : 0), + rngState(0), watchpointMap(NULL), scriptCountsMap(NULL), debugScriptMap(NULL), @@ -128,6 +129,9 @@ JSCompartment::init(JSContext *cx) if (!regExps.init(cx)) return false; + if (cx) + InitRandom(cx->runtime, &rngState); + #ifdef JSGC_GENERATIONAL /* * If we are in the middle of post-barrier verification, we need to diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index af7bf427204..39f75b983e7 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -470,6 +470,9 @@ struct JSCompartment : private JS::shadow::Compartment, public js::gc::GraphNode js::DtoaCache dtoaCache; + /* Random number generator state, used by jsmath.cpp. */ + uint64_t rngState; + private: /* * Weak reference to each global in this compartment that is a debuggee. diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp index d1faaeb627a..5cf1b54a683 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -517,47 +517,48 @@ js_math_pow(JSContext *cx, unsigned argc, Value *vp) # pragma optimize("", on) #endif -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 uint64_t RNG_MULTIPLIER = 0x5DEECE66DLL; +static const uint64_t RNG_ADDEND = 0xBLL; +static const uint64_t RNG_MASK = (1LL << 48) - 1; static const double RNG_DSCALE = double(1LL << 53); /* * Math.random() support, lifted from java.util.Random.java. */ extern void -random_setSeed(int64_t *rngSeed, int64_t seed) +random_setSeed(uint64_t *rngState, uint64_t seed) { - *rngSeed = (seed ^ RNG_MULTIPLIER) & RNG_MASK; + *rngState = (seed ^ RNG_MULTIPLIER) & RNG_MASK; } void -js_InitRandom(JSContext *cx) +js::InitRandom(JSRuntime *rt, uint64_t *rngState) { /* - * 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. + * Set the seed from current time. Since we have a RNG per compartment and + * we often bring up several compartments at the same time, mix in a + * different integer each time. This is only meant to prevent all the new + * compartments from getting the same sequence of pseudo-random + * numbers. There's no security guarantee. */ - random_setSeed(&cx->rngSeed, (PRMJ_Now() / 1000) ^ int64_t(cx) ^ int64_t(cx->getNext())); + random_setSeed(rngState, (uint64_t(PRMJ_Now()) << 8) ^ rt->nextRNGNonce()); } extern uint64_t -random_next(int64_t *rngSeed, int bits) +random_next(uint64_t *rngState, int bits) { - uint64_t nextseed = *rngSeed * RNG_MULTIPLIER; - nextseed += RNG_ADDEND; - nextseed &= RNG_MASK; - *rngSeed = nextseed; - return nextseed >> (48 - bits); + uint64_t nextstate = *rngState * RNG_MULTIPLIER; + nextstate += RNG_ADDEND; + nextstate &= RNG_MASK; + *rngState = nextstate; + return nextstate >> (48 - bits); } static inline double random_nextDouble(JSContext *cx) { - return double((random_next(&cx->rngSeed, 26) << 27) + random_next(&cx->rngSeed, 27)) / - RNG_DSCALE; + uint64_t *rng = &cx->compartment->rngState; + return double((random_next(rng, 26) << 27) + random_next(rng, 27)) / RNG_DSCALE; } double diff --git a/js/src/jsmath.h b/js/src/jsmath.h index 832c83a1c1d..29900f3e6bd 100644 --- a/js/src/jsmath.h +++ b/js/src/jsmath.h @@ -45,6 +45,9 @@ class MathCache size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf); }; +extern void +InitRandom(JSRuntime *rt, uint64_t *rngState); + } /* namespace js */ /* @@ -54,9 +57,6 @@ class MathCache extern JSObject * js_InitMathClass(JSContext *cx, js::HandleObject obj); -extern void -js_InitRandom(JSContext *cx); - extern double math_random_no_outparam(JSContext *cx);