Bug 1440539 Support time jitter in the JS Shell, and expose a function to enable it r?luke
This adds jittering to the already existing logic for time clamping. It also exposes a
testing function allowing those interested to enable time clamping or time clamping and
jittering.
Neither (clamping nor jittering) is enabled by default.
MozReview-Commit-ID: JcHCEwRQPch
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -4927,16 +4927,43 @@ IsLegacyIterator(JSContext* cx, unsigned
if (args.length() < 1)
args.rval().setBoolean(false);
else
args.rval().setBoolean(IsPropertyIterator(args[0]));
return true;
}
static bool
+SetTimeResolution(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ RootedObject callee(cx, &args.callee());
+
+ if (!args.requireAtLeast(cx, "setTimeResolution", 2))
+ return false;
+
+ if (!args[0].isInt32()) {
+ ReportUsageErrorASCII(cx, callee, "First argument must be an Int32.");
+ return false;
+ }
+ int32_t resolution = args[0].toInt32();
+
+ if (!args[1].isBoolean()) {
+ ReportUsageErrorASCII(cx, callee, "Second argument must be a Boolean");
+ return false;
+ }
+ bool jitter = args[1].toBoolean();
+
+ JS::SetTimeResolutionUsec(resolution, jitter);
+
+ args.rval().setUndefined();
+ return true;
+}
+
+static bool
EnableExpressionClosures(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
JS::ContextOptionsRef(cx).setExpressionClosures(true);
args.rval().setUndefined();
return true;
}
@@ -5688,16 +5715,21 @@ gc::ZealModeHelpText),
JS_FN_HELP("isLegacyIterator", IsLegacyIterator, 1, 0,
"isLegacyIterator(value)",
" Returns whether the value is considered is a legacy iterator.\n"),
JS_FN_HELP("getTimeZone", GetTimeZone, 0, 0,
"getTimeZone()",
" Get the current time zone.\n"),
+ JS_FN_HELP("setTimeResolution", SetTimeResolution, 2, 0,
+"setTimeResolution(resolution, jitter)",
+" Enables time clamping and jittering. Specify a time resolution in\n"
+" microseconds and whether or not to jitter\n"),
+
JS_FN_HELP("enableExpressionClosures", EnableExpressionClosures, 0, 0,
"enableExpressionClosures()",
" Enables the deprecated, non-standard expression closures.\n"),
JS_FN_HELP("disableExpressionClosures", DisableExpressionClosures, 0, 0,
"disableExpressionClosures()",
" Disables the deprecated, non-standard expression closures.\n"),
old mode 100644
new mode 100755
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -1305,18 +1305,47 @@ date_parse(JSContext* cx, unsigned argc,
}
static ClippedTime
NowAsMillis()
{
double now = PRMJ_Now();
if (sReduceMicrosecondTimePrecisionCallback)
now = sReduceMicrosecondTimePrecisionCallback(now);
- else if (sResolutionUsec)
- now = floor(now / sResolutionUsec) * sResolutionUsec;
+ else if (sResolutionUsec) {
+ double clamped = floor(now / sResolutionUsec) * sResolutionUsec;
+
+ if (sJitter) {
+ // Calculate a random midpoint for jittering. In the browser, we are adversarial:
+ // Web Content may try to calculate the midpoint themselves and use that to bypass
+ // it's security. In the JS Shell, we are not adversarial, we want to jitter the
+ // time to recreate the operating environment, but we do not concern ourselves
+ // with trying to prevent an attacker from calculating the midpoint themselves.
+ // So we use a very simple, very fast CRC with a hardcoded seed.
+
+ uint64_t midpoint = *((uint64_t*)&clamped);
+ midpoint ^= 0x0F00DD1E2BAD2DED; // XOR in a 'secret'
+ // MurmurHash3 internal component from
+ // https://searchfox.org/mozilla-central/rev/61d400da1c692453c2dc2c1cf37b616ce13dea5b/dom/canvas/MurmurHash3.cpp#85
+ midpoint ^= midpoint >> 33;
+ midpoint *= uint64_t{0xFF51AFD7ED558CCD};
+ midpoint ^= midpoint >> 33;
+ midpoint *= uint64_t{0xC4CEB9FE1A85EC53};
+ midpoint ^= midpoint >> 33;
+ midpoint %= sResolutionUsec;
+
+ if (now > clamped + midpoint) { // We're jittering up to the next step
+ now = clamped + sResolutionUsec;
+ } else { // We're staying at the clamped value
+ now = clamped;
+ }
+ } else { //No jitter, only clamping
+ now = clamped;
+ }
+ }
return TimeClip(now / PRMJ_USEC_PER_MSEC);
}
bool
js::date_now(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);