Re-seed Math.random() for each window/frame/context (475585, r=waldo,dolske, a=beltzner).
authorAndreas Gal <gal@mozilla.com>
Thu, 18 Mar 2010 08:27:26 -0700
changeset 34060 23e155b902acd2c166095153add525c4d93b982e
parent 34059 1a9c3eed5fe1733ba646d05b8c40293d6978ad9e
child 34061 15baccaaa0536b189020ceb1b8a5f06b36fdb256
push id1247
push userreed@reedloden.com
push dateTue, 13 Apr 2010 04:36:48 +0000
reviewerswaldo, dolske, beltzner
bugs475585
milestone1.9.2.4pre
Re-seed Math.random() for each window/frame/context (475585, r=waldo,dolske, a=beltzner).
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsmath.cpp
js/src/jsmath.h
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -80,17 +80,16 @@ InitThreadData(JSThreadData *data)
 #ifdef DEBUG
     /* The data must be already zeroed. */
     for (size_t i = 0; i != sizeof(*data); ++i)
         JS_ASSERT(reinterpret_cast<uint8*>(data)[i] == 0);
 #endif
 #ifdef JS_TRACER
     js_InitJIT(&data->traceMonitor);
 #endif
-    js_InitRandom(data);
 }
 
 static void
 FinishThreadData(JSThreadData *data)
 {
 #ifdef DEBUG
     /* All GC-related things must be already removed at this point. */
     for (size_t i = 0; i != JS_ARRAY_LENGTH(data->scriptsToGC); ++i)
@@ -482,16 +481,18 @@ js_NewContext(JSRuntime *rt, size_t stac
          * when we exit the loop with the first flag set to true, that GC is
          * finished.
          */
         js_WaitForGC(rt);
     }
     JS_APPEND_LINK(&cx->link, &rt->contextList);
     JS_UNLOCK_GC(rt);
 
+    js_InitRandom(cx);
+
     /*
      * If cx is the first context on this runtime, initialize well-known atoms,
      * keywords, numbers, and strings.  If one of these steps should fail, the
      * runtime will be left in a partially initialized state, with zeroes and
      * nulls stored in the default-initialized remainder of the struct.  We'll
      * clean the runtime up under js_DestroyContext, because cx will be "last"
      * as well as "first".
      */
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -279,19 +279,16 @@ struct JSThreadData {
      * The GSN cache is per thread since even multi-cx-per-thread embeddings
      * do not interleave js_GetSrcNote calls.
      */
     JSGSNCache          gsnCache;
 
     /* Property cache for faster call/get/set invocation. */
     JSPropertyCache     propertyCache;
 
-    /* Random number generator state, used by jsmath.cpp. */
-    int64               rngSeed;
-
 #ifdef JS_TRACER
     /* Trace-tree JIT recorder/interpreter state. */
     JSTraceMonitor      traceMonitor;
 #endif
 
     /* Lock-free hashed lists of scripts created by eval to garbage-collect. */
     JSScript            *scriptsToGC[JS_EVAL_CACHE_SIZE];
 
@@ -1121,16 +1118,19 @@ struct JSContext {
     JSSecurityCallbacks *securityCallbacks;
 
     /* Pinned regexp pool used for regular expressions. */
     JSArenaPool         regexpPool;
 
     /* Stored here to avoid passing it around as a parameter. */
     uintN               resolveFlags;
 
+    /* Random number generator state, used by jsmath.cpp. */
+    int64               rngSeed;
+
 #ifdef JS_TRACER
     /*
      * State for the current tree execution.  bailExit is valid if the tree has
      * called back into native code via a _FAIL builtin and has not yet bailed,
      * else garbage (NULL in debug builds).
      */
     InterpState         *interpState;
     VMSideExit          *bailExit;
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -431,48 +431,56 @@ static const int64 RNG_MULTIPLIER = 0x5D
 static const int64 RNG_ADDEND = 0xBLL;
 static const int64 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(JSThreadData *data, int64 seed)
+random_setSeed(JSContext *cx, int64 seed)
 {
-    data->rngSeed = (seed ^ RNG_MULTIPLIER) & RNG_MASK;
+    cx->rngSeed = (seed ^ RNG_MULTIPLIER) & RNG_MASK;
 }
 
 void
-js_InitRandom(JSThreadData *data)
+js_InitRandom(JSContext *cx)
 {
-    /* Finally, set the seed from current time. */
-    random_setSeed(data, PRMJ_Now() / 1000);
+    /*
+     * 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(cx) ^
+                   int64(cx->link.next));
 }
 
 static inline uint64
-random_next(JSThreadData *data, int bits)
+random_next(JSContext *cx, int bits)
 {
-    uint64 nextseed = data->rngSeed * RNG_MULTIPLIER;
+    uint64 nextseed = cx->rngSeed * RNG_MULTIPLIER;
     nextseed += RNG_ADDEND;
     nextseed &= RNG_MASK;
-    data->rngSeed = nextseed;
+    cx->rngSeed = nextseed;
     return nextseed >> (48 - bits);
 }
 
 static inline jsdouble
-random_nextDouble(JSThreadData *data)
+random_nextDouble(JSContext *cx)
 {
-    return jsdouble((random_next(data, 26) << 27) + random_next(data, 27)) / RNG_DSCALE;
+    return jsdouble((random_next(cx, 26) << 27) + random_next(cx, 27)) / RNG_DSCALE;
 }
 
 static JSBool
 math_random(JSContext *cx, uintN argc, jsval *vp)
 {
-    jsdouble z = random_nextDouble(JS_THREAD_DATA(cx));
+    jsdouble z = random_nextDouble(cx);
     return js_NewNumberInRootedValue(cx, z, vp);
 }
 
 #if defined _WIN32 && !defined WINCE && _MSC_VER < 1400
 /* Try to work around apparent _copysign bustage in VC6 and VC7. */
 double
 js_copysign(double x, double y)
 {
@@ -669,17 +677,17 @@ math_pow_tn(jsdouble d, jsdouble p)
     if (p == 0)
         return 1.0;
     return pow(d, p);
 }
 
 static jsdouble FASTCALL
 math_random_tn(JSContext *cx)
 {
-    return random_nextDouble(JS_THREAD_DATA(cx));
+    return random_nextDouble(cx);
 }
 
 static jsdouble FASTCALL
 math_round_tn(jsdouble x)
 {
     return js_copysign(floor(x + 0.5), x);
 }
 
--- a/js/src/jsmath.h
+++ b/js/src/jsmath.h
@@ -46,17 +46,17 @@
 JS_BEGIN_EXTERN_C
 
 extern JSClass js_MathClass;
 
 extern JSObject *
 js_InitMathClass(JSContext *cx, JSObject *obj);
 
 extern void
-js_InitRandom(JSThreadData *data);
+js_InitRandom(JSContext *cx);
 
 extern JSBool
 js_math_ceil(JSContext *cx, uintN argc, jsval *vp);
 
 extern JSBool
 js_math_floor(JSContext *cx, uintN argc, jsval *vp);
 
 extern JSBool