diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 302045de0bd8..23fe92ada4b3 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -888,7 +888,8 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads) threadPool(this), 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 40687f325961..de2e8bd58101 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -262,8 +262,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 @@ -1114,7 +1112,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 2da7ac4b43eb..284e4fef4823 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1219,6 +1219,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. */ @@ -1567,9 +1577,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 bd7ac27f0a5a..8e539e3e0acf 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -78,6 +78,7 @@ JSCompartment::JSCompartment(JSRuntime *rt) gcGrayRoots(), gcMallocBytes(0), debugModeBits(rt->debugMode ? DebugFromC : 0), + rngState(0), watchpointMap(NULL), scriptCountsMap(NULL), debugScriptMap(NULL), @@ -126,6 +127,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 5906207ccf02..32a7ed359990 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -463,6 +463,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 22d4ef88a685..923849cff61f 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -541,47 +541,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 832c83a1c1d2..29900f3e6bde 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);