Bug 820180 - Isolate JS pseudorandom number generator state per compartment. r=luke.

--HG--
extra : rebase_source : 966bf5fd222b342a3ede69c68cb95d97568d98b7
This commit is contained in:
Jason Orendorff 2012-12-14 14:27:22 -06:00
parent 243545fcd7
commit 4dc4213e81
7 changed files with 42 additions and 29 deletions

View File

@ -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);

View File

@ -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),

View File

@ -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;

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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);