Bug 1443943 Do not clamp/jitter in the JS Engine if it's system context r?jorendorff draft
authorTom Ritter <tom@mozilla.com>
Fri, 09 Mar 2018 09:49:28 -0600
changeset 767536 561bf8790e37def5d7abfe97c10d0ea48f60df4b
parent 767535 d5f7685a6242783e830e9ac25a68fe973eb61662
child 767537 d89b40edc60cb580ee170c7c2e10315722696067
push id102629
push userbmo:tom@mozilla.com
push dateWed, 14 Mar 2018 19:27:31 +0000
reviewersjorendorff
bugs1443943
milestone61.0a1
Bug 1443943 Do not clamp/jitter in the JS Engine if it's system context r?jorendorff MozReview-Commit-ID: LqL7xaYoHCT
dom/base/nsGlobalWindowOuter.cpp
dom/workers/WorkerPrivate.cpp
js/src/jsapi.h
js/src/jsdate.cpp
js/xpconnect/src/Sandbox.cpp
js/xpconnect/src/nsXPConnect.cpp
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -1639,16 +1639,17 @@ CreateNativeGlobalForInner(JSContext* aC
 
   SelectZoneGroup(aNewInner, options.creationOptions());
 
   // Sometimes add-ons load their own XUL windows, either as separate top-level
   // windows or inside a browser element. In such cases we want to tag the
   // window's compartment with the add-on ID. See bug 1092156.
   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
     options.creationOptions().setAddonId(MapURIToAddonID(aURI));
+    options.creationOptions().setClampAndJitterTime(false);
   }
 
   options.creationOptions().setSecureContext(aIsSecureContext);
 
   xpc::InitGlobalObjectOptions(options, aPrincipal);
 
   // Determine if we need the Components object.
   bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) ||
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2655,18 +2655,22 @@ WorkerPrivate::WorkerPrivate(WorkerPriva
     } else {
       MOZ_ASSERT_UNREACHABLE("non-chrome worker that is not a service worker "
                              "that has no parent and no associated window");
     }
 
     if (mIsSecureContext) {
       mJSSettings.chrome.compartmentOptions
                  .creationOptions().setSecureContext(true);
+      mJSSettings.chrome.compartmentOptions
+                 .creationOptions().setClampAndJitterTime(false);
       mJSSettings.content.compartmentOptions
                  .creationOptions().setSecureContext(true);
+      mJSSettings.content.compartmentOptions
+                 .creationOptions().setClampAndJitterTime(false);
     }
 
     mIsInAutomation = xpc::IsInAutomation();
 
     // Our parent can get suspended after it initiates the async creation
     // of a new worker thread.  In this case suspend the new worker as well.
     if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsSuspended()) {
       ParentWindowPaused();
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1990,17 +1990,18 @@ class JS_PUBLIC_API(CompartmentCreationO
         traceGlobal_(nullptr),
         zoneSpec_(NewZoneInSystemZoneGroup),
         zonePointer_(nullptr),
         invisibleToDebugger_(false),
         mergeable_(false),
         preserveJitCode_(false),
         cloneSingletons_(false),
         sharedMemoryAndAtomics_(false),
-        secureContext_(false)
+        secureContext_(false),
+        clampAndJitterTime_(true)
     {}
 
     // A null add-on ID means that the compartment is not associated with an
     // add-on.
     JSAddonId* addonIdOrNull() const { return addonId_; }
     CompartmentCreationOptions& setAddonId(JSAddonId* id) {
         addonId_ = id;
         return *this;
@@ -2066,27 +2067,34 @@ class JS_PUBLIC_API(CompartmentCreationO
     // https://w3c.github.io/webappsec-secure-contexts/
     // https://bugzilla.mozilla.org/show_bug.cgi?id=1162772#c34
     bool secureContext() const { return secureContext_; }
     CompartmentCreationOptions& setSecureContext(bool flag) {
         secureContext_ = flag;
         return *this;
     }
 
+    bool clampAndJitterTime() const { return clampAndJitterTime_; }
+    CompartmentCreationOptions& setClampAndJitterTime(bool flag) {
+        clampAndJitterTime_ = flag;
+        return *this;
+    }
+
   private:
     JSAddonId* addonId_;
     JSTraceOp traceGlobal_;
     ZoneSpecifier zoneSpec_;
     void* zonePointer_; // Per zoneSpec_, either a Zone, ZoneGroup, or null.
     bool invisibleToDebugger_;
     bool mergeable_;
     bool preserveJitCode_;
     bool cloneSingletons_;
     bool sharedMemoryAndAtomics_;
     bool secureContext_;
+    bool clampAndJitterTime_;
 };
 
 /**
  * CompartmentBehaviors specifies behaviors of a compartment that can be
  * changed after the compartment's been created.
  */
 class JS_PUBLIC_API(CompartmentBehaviors)
 {
old mode 100755
new mode 100644
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -1300,22 +1300,23 @@ date_parse(JSContext* cx, unsigned argc,
         return true;
     }
 
     args.rval().set(TimeValue(result));
     return true;
 }
 
 static ClippedTime
-NowAsMillis()
+NowAsMillis(JSContext* cx)
 {
     double now = PRMJ_Now();
-    if (sReduceMicrosecondTimePrecisionCallback)
+    bool clampAndJitter = JS::CompartmentCreationOptionsRef(js::GetContextCompartment(cx)).clampAndJitterTime();
+    if (clampAndJitter && sReduceMicrosecondTimePrecisionCallback)
         now = sReduceMicrosecondTimePrecisionCallback(now);
-    else if (sResolutionUsec) {
+    else if (clampAndJitter && 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.
@@ -1344,17 +1345,17 @@ NowAsMillis()
 
     return TimeClip(now / PRMJ_USEC_PER_MSEC);
 }
 
 bool
 js::date_now(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    args.rval().set(TimeValue(NowAsMillis()));
+    args.rval().set(TimeValue(NowAsMillis(cx)));
     return true;
 }
 
 void
 DateObject::setUTCTime(ClippedTime t)
 {
     for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++)
         setReservedSlot(ind, UndefinedValue());
@@ -3115,17 +3116,17 @@ ToDateString(JSContext* cx, const CallAr
     return FormatDate(cx, t.toDouble(), FormatSpec::DateTime, args.rval());
 }
 
 static bool
 DateNoArguments(JSContext* cx, const CallArgs& args)
 {
     MOZ_ASSERT(args.length() == 0);
 
-    ClippedTime now = NowAsMillis();
+    ClippedTime now = NowAsMillis(cx);
 
     if (args.isConstructing())
         return NewDateObject(cx, args, now);
 
     return ToDateString(cx, args, now);
 }
 
 static bool
@@ -3166,17 +3167,17 @@ DateOneArgument(JSContext* cx, const Cal
             if (!ToNumber(cx, args[0], &d))
                 return false;
             t = TimeClip(d);
         }
 
         return NewDateObject(cx, args, t);
     }
 
-    return ToDateString(cx, args, NowAsMillis());
+    return ToDateString(cx, args, NowAsMillis(cx));
 }
 
 static bool
 DateMultipleArguments(JSContext* cx, const CallArgs& args)
 {
     MOZ_ASSERT(args.length() >= 2);
 
     // Step 3.
@@ -3246,17 +3247,17 @@ DateMultipleArguments(JSContext* cx, con
 
         // Step 3p.
         double finalDate = MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli));
 
         // Steps 3q-t.
         return NewDateObject(cx, args, TimeClip(UTC(finalDate)));
     }
 
-    return ToDateString(cx, args, NowAsMillis());
+    return ToDateString(cx, args, NowAsMillis(cx));
 }
 
 bool
 js::DateConstructor(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0)
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -1119,16 +1119,19 @@ xpc::CreateSandboxObject(JSContext* cx, 
     JS::CompartmentOptions compartmentOptions;
 
     auto& creationOptions = compartmentOptions.creationOptions();
 
     // XXXjwatt: Consider whether/when sandboxes should be able to see
     // [SecureContext] API (bug 1273687).  In that case we'd call
     // creationOptions.setSecureContext(true).
 
+    if (principal == nsXPConnect::SystemPrincipal())
+        creationOptions.setClampAndJitterTime(false);
+
     if (xpc::SharedMemoryEnabled())
         creationOptions.setSharedMemoryAndAtomicsEnabled(true);
 
     if (options.sameZoneAs)
         creationOptions.setExistingZone(js::UncheckedUnwrap(options.sameZoneAs));
     else if (options.freshZone)
         creationOptions.setNewZoneInSystemZoneGroup();
     else
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -503,16 +503,17 @@ InitGlobalObjectOptions(JS::CompartmentO
     bool shouldDiscardSystemSource = ShouldDiscardSystemSource();
     bool extraWarningsForSystemJS = ExtraWarningsForSystemJS();
 
     bool isSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
 
     if (isSystem) {
         // Make sure [SecureContext] APIs are visible:
         aOptions.creationOptions().setSecureContext(true);
+        aOptions.creationOptions().setClampAndJitterTime(false);
     }
 
     if (shouldDiscardSystemSource) {
         bool discardSource = isSystem;
 
         aOptions.behaviors().setDiscardSource(discardSource);
     }