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

This commit is contained in:
Jason Orendorff 2012-12-14 14:27:22 -06:00
parent 1aa3ee7e8c
commit ca9e2f24e6
9 changed files with 46 additions and 33 deletions

View File

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

View File

@ -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()
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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