diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-01-15 11:13:11 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-01-15 11:39:43 +0000 |
commit | b7a9c9bf5b6bc08ae8746b7679317a9027868681 (patch) | |
tree | 73dd8ca522e152927d230faff2ecd2836823e710 | |
parent | 1f43353c961a58cee76faf11f1be22c182f7dffd (diff) |
[Backport] Clamp performance.now() to 100us.v5.9.4
This patch reduces the resolution of performance.now() from 5us to 100us
and adds pseudorandom jitter on top.
TBR=skyostil@chromium.org
(cherry picked from commit a77687fd89adc1bc2ce91921456e0b9b59388120)
Authors: Ross McIlroy <rmcilroy@chromium.org>, Sami Kyostila <skyostil@chromium.org>
Bug: 798964
Reviewed-on: https://chromium-review.googlesource.com/849993
Commit-Queue: Sami Kyöstilä <skyostil@chromium.org>
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: Jochen Eisinger <jochen@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#527008}
Reviewed-on: https://chromium-review.googlesource.com/853505
Reviewed-by: Sami Kyöstilä <skyostil@chromium.org>
Cr-Commit-Position: refs/branch-heads/3282@{#439}
Cr-Branched-From: 5fdc0fab22ce7efd32532ee989b223fa12f8171e-refs/heads/master@{#520840}
Change-Id: Ia7e1171e1505ddc73cb5356fcc0aac2466f49e08
Reviewed-by: Michael Brüning <michael.bruning@qt.io>
9 files changed, 207 insertions, 9 deletions
diff --git a/chromium/third_party/WebKit/Source/core/dom/IdleDeadline.cpp b/chromium/third_party/WebKit/Source/core/dom/IdleDeadline.cpp index f35300d57e5..66e0f2afeb1 100644 --- a/chromium/third_party/WebKit/Source/core/dom/IdleDeadline.cpp +++ b/chromium/third_party/WebKit/Source/core/dom/IdleDeadline.cpp @@ -15,7 +15,7 @@ IdleDeadline::IdleDeadline(double deadlineSeconds, CallbackType callbackType) double IdleDeadline::timeRemaining() const { double timeRemaining = m_deadlineSeconds - monotonicallyIncreasingTime(); if (timeRemaining < 0) - timeRemaining = 0; + return 0; return 1000.0 * PerformanceBase::clampTimeResolution(timeRemaining); } diff --git a/chromium/third_party/WebKit/Source/core/dom/ScriptedIdleTaskController.cpp b/chromium/third_party/WebKit/Source/core/dom/ScriptedIdleTaskController.cpp index dc267a3db88..f7192b82366 100644 --- a/chromium/third_party/WebKit/Source/core/dom/ScriptedIdleTaskController.cpp +++ b/chromium/third_party/WebKit/Source/core/dom/ScriptedIdleTaskController.cpp @@ -34,7 +34,6 @@ class IdleRequestCallbackWrapper static void idleTaskFired( PassRefPtr<IdleRequestCallbackWrapper> callbackWrapper, double deadlineSeconds) { - // TODO(rmcilroy): Implement clamping of deadline in some form. if (ScriptedIdleTaskController* controller = callbackWrapper->controller()) controller->callbackFired(callbackWrapper->id(), deadlineSeconds, IdleDeadline::CallbackType::CalledWhenIdle); diff --git a/chromium/third_party/WebKit/Source/core/timing/PerformanceBase.cpp b/chromium/third_party/WebKit/Source/core/timing/PerformanceBase.cpp index 88094fa06e3..f23f6695234 100644 --- a/chromium/third_party/WebKit/Source/core/timing/PerformanceBase.cpp +++ b/chromium/third_party/WebKit/Source/core/timing/PerformanceBase.cpp @@ -39,6 +39,7 @@ #include "core/timing/PerformanceResourceTiming.h" #include "core/timing/PerformanceUserTiming.h" #include "platform/network/ResourceTimingInfo.h" +#include "platform/TimeClamper.h" #include "platform/weborigin/SecurityOrigin.h" #include "wtf/CurrentTime.h" #include <algorithm> @@ -436,9 +437,9 @@ void PerformanceBase::deliverObservationsTimerFired(TimerBase*) { } // static -double PerformanceBase::clampTimeResolution(double timeSeconds) { - const double resolutionSeconds = 0.000005; - return floor(timeSeconds / resolutionSeconds) * resolutionSeconds; +double PerformanceBase::clampTimeResolution(double time_seconds) { + DEFINE_THREAD_SAFE_STATIC_LOCAL(TimeClamper, clamper, new TimeClamper()); + return clamper.ClampTimeResolution(time_seconds); } DOMHighResTimeStamp PerformanceBase::monotonicTimeToDOMHighResTimeStamp( @@ -447,9 +448,9 @@ DOMHighResTimeStamp PerformanceBase::monotonicTimeToDOMHighResTimeStamp( if (m_timeOrigin == 0.0) return 0.0; - double timeInSeconds = monotonicTime - m_timeOrigin; - return convertSecondsToDOMHighResTimeStamp( - clampTimeResolution(timeInSeconds)); + double clamped_time_in_seconds = + clampTimeResolution(monotonicTime) - clampTimeResolution(m_timeOrigin); + return convertSecondsToDOMHighResTimeStamp(clamped_time_in_seconds); } double PerformanceBase::monotonicTimeToDOMHighResTimeStampInMillis( diff --git a/chromium/third_party/WebKit/Source/core/timing/PerformanceBase.h b/chromium/third_party/WebKit/Source/core/timing/PerformanceBase.h index 52a1c9f2dad..05271ad793b 100644 --- a/chromium/third_party/WebKit/Source/core/timing/PerformanceBase.h +++ b/chromium/third_party/WebKit/Source/core/timing/PerformanceBase.h @@ -65,7 +65,7 @@ class CORE_EXPORT PerformanceBase : public EventTargetWithInlineData { virtual void updateLongTaskInstrumentation() {} - // Reduce the resolution to 5µs to prevent timing attacks. See: + // Reduce the resolution to prevent timing attacks. See: // http://www.w3.org/TR/hr-time-2/#privacy-security static double clampTimeResolution(double timeSeconds); diff --git a/chromium/third_party/WebKit/Source/platform/BUILD.gn b/chromium/third_party/WebKit/Source/platform/BUILD.gn index 2c35eed91ea..7f8c5fca5a2 100644 --- a/chromium/third_party/WebKit/Source/platform/BUILD.gn +++ b/chromium/third_party/WebKit/Source/platform/BUILD.gn @@ -315,6 +315,8 @@ component("platform") { "Theme.cpp", "Theme.h", "ThemeTypes.h", + "TimeClamper.cpp", + "TimeClamper.h", "Timer.cpp", "Timer.h", "UUID.cpp", @@ -1644,6 +1646,7 @@ test("blink_platform_unittests") { "PODRedBlackTreeTest.cpp", "ScopedOrientationChangeIndicatorTest.cpp", "SharedBufferTest.cpp", + "TimeClamperTest.cpp", "TimerTest.cpp", "UUIDTest.cpp", "UserGestureIndicatorTest.cpp", diff --git a/chromium/third_party/WebKit/Source/platform/DEPS b/chromium/third_party/WebKit/Source/platform/DEPS index 2003c6e2f92..a91929d6dfe 100644 --- a/chromium/third_party/WebKit/Source/platform/DEPS +++ b/chromium/third_party/WebKit/Source/platform/DEPS @@ -4,6 +4,7 @@ include_rules = [ "+base/bind.h", "+base/callback.h", "+base/callback_forward.h", + "+base/bit_cast.h", "+base/files", "+base/guid.h", "+base/json", diff --git a/chromium/third_party/WebKit/Source/platform/TimeClamper.cpp b/chromium/third_party/WebKit/Source/platform/TimeClamper.cpp new file mode 100644 index 00000000000..3eb6815018c --- /dev/null +++ b/chromium/third_party/WebKit/Source/platform/TimeClamper.cpp @@ -0,0 +1,54 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/TimeClamper.h" + +#include "base/bit_cast.h" +#include "wtf/Assertions.h" +#include "wtf/CryptographicallyRandomNumber.h" + +#include <cmath> + +namespace blink { + +TimeClamper::TimeClamper() { + cryptographicallyRandomValues(&secret_, sizeof(secret_)); +} + +double TimeClamper::ClampTimeResolution(double time_seconds) const { + DCHECK_GE(time_seconds, 0); + double clamped_time = + floor(time_seconds / kResolutionSeconds) * kResolutionSeconds; + double tick_threshold = ThresholdFor(clamped_time); + + if (time_seconds >= tick_threshold) + return clamped_time + kResolutionSeconds; + return clamped_time; +} + +inline double TimeClamper::ThresholdFor(double clamped_time) const { + uint64_t time_hash = MurmurHash3(bit_cast<int64_t>(clamped_time) ^ secret_); + return clamped_time + kResolutionSeconds * ToDouble(time_hash); +} + +// static +inline double TimeClamper::ToDouble(uint64_t value) { + // Exponent for double values for [1.0 .. 2.0] + static const uint64_t kExponentBits = uint64_t{0x3FF0000000000000}; + static const uint64_t kMantissaMask = uint64_t{0x000FFFFFFFFFFFFF}; + uint64_t random = (value & kMantissaMask) | kExponentBits; + return bit_cast<double>(random) - 1; +} + +// static +inline uint64_t TimeClamper::MurmurHash3(uint64_t value) { + value ^= value >> 33; + value *= uint64_t{0xFF51AFD7ED558CCD}; + value ^= value >> 33; + value *= uint64_t{0xC4CEB9FE1A85EC53}; + value ^= value >> 33; + return value; +} + +} // namespace blink diff --git a/chromium/third_party/WebKit/Source/platform/TimeClamper.h b/chromium/third_party/WebKit/Source/platform/TimeClamper.h new file mode 100644 index 00000000000..7550ef1ff23 --- /dev/null +++ b/chromium/third_party/WebKit/Source/platform/TimeClamper.h @@ -0,0 +1,42 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TimeClamper_h +#define TimeClamper_h + +#include "base/macros.h" +#include "platform/PlatformExport.h" + +#include <stdint.h> + +namespace blink { + +class PLATFORM_EXPORT TimeClamper { + public: + static constexpr double kResolutionSeconds = 0.0001; + + TimeClamper(); + + // Deterministically clamp the time value |time_seconds| to a 100us interval + // to prevent timing attacks. See + // http://www.w3.org/TR/hr-time-2/#privacy-security. + // + // For each clamped time interval, we compute a pseudorandom transition + // threshold. The returned time will either be the start of that interval or + // the next one depending on which side of the threshold |time_seconds| is. + double ClampTimeResolution(double time_seconds) const; + + private: + inline double ThresholdFor(double clamped_time) const; + static inline double ToDouble(uint64_t value); + static inline uint64_t MurmurHash3(uint64_t value); + + uint64_t secret_; + + DISALLOW_COPY_AND_ASSIGN(TimeClamper); +}; + +} // namespace blink + +#endif // TimeClamper_h diff --git a/chromium/third_party/WebKit/Source/platform/TimeClamperTest.cpp b/chromium/third_party/WebKit/Source/platform/TimeClamperTest.cpp new file mode 100644 index 00000000000..04e4622c152 --- /dev/null +++ b/chromium/third_party/WebKit/Source/platform/TimeClamperTest.cpp @@ -0,0 +1,98 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/TimeClamper.h" + +#include "testing/gtest/include/gtest/gtest.h" + +#include <cmath> + +namespace blink { +namespace { +const double kInterval = TimeClamper::kResolutionSeconds; +} + +TEST(TimeClamperTest, TimeStampsAreNonNegative) { + TimeClamper clamper; + EXPECT_GE(clamper.ClampTimeResolution(0), 0.f); + EXPECT_GE(clamper.ClampTimeResolution(TimeClamper::kResolutionSeconds), 0.f); +} + +TEST(TimeClamperTest, TimeStampsIncreaseByFixedAmount) { + const double kEpsilon = 1e-10; + TimeClamper clamper; + double prev = clamper.ClampTimeResolution(0); + for (double time_seconds = 0; time_seconds < kInterval * 100; + time_seconds += kInterval * 0.1) { + double clamped_time = clamper.ClampTimeResolution(time_seconds); + double delta = clamped_time - prev; + if (delta > kEpsilon) { + ASSERT_TRUE(std::fabs(delta - kInterval) < kEpsilon); + prev = clamped_time; + } + } +} + +TEST(TimeClamperTest, ClampingIsConsistent) { + TimeClamper clamper; + for (double time_seconds = 0; time_seconds < kInterval * 100; + time_seconds += kInterval * 0.1) { + double t1 = clamper.ClampTimeResolution(time_seconds); + double t2 = clamper.ClampTimeResolution(time_seconds); + EXPECT_EQ(t1, t2); + } +} + +TEST(TimeClamperTest, ClampingIsPerInstance) { + const double kEpsilon = 1e-10; + TimeClamper clamper1; + TimeClamper clamper2; + double time_seconds = 0; + while (true) { + if (std::fabs(clamper1.ClampTimeResolution(time_seconds) - + clamper2.ClampTimeResolution(time_seconds)) > kEpsilon) { + break; + } + time_seconds += kInterval; + } +} + +TEST(TimeClamperTest, ClampingIsUniform) { + const int kBuckets = 8; + const int kSampleCount = 10000; + const double kEpsilon = 1e-10; + const double kTimeStep = kInterval / kBuckets; + double time_seconds = 299792.458; + int histogram[kBuckets] = {0}; + TimeClamper clamper; + + // This test ensures the jitter thresholds are approximately uniformly + // distributed inside the clamping intervals. It samples individual intervals + // to detect where the threshold is and counts the number of steps taken. + for (int i = 0; i < kSampleCount; i++) { + double start = clamper.ClampTimeResolution(time_seconds); + for (int step = 0; step < kBuckets; step++) { + time_seconds += kTimeStep; + if (std::abs(clamper.ClampTimeResolution(time_seconds) - start) > + kEpsilon) { + histogram[step]++; + // Skip to the next interval to make sure each measurement is + // independent. + time_seconds = floor(time_seconds / kInterval) * kInterval + kInterval; + break; + } + } + } + + double expected_count = kSampleCount / kBuckets; + double chi_squared = 0; + for (int i = 0; i < kBuckets; ++i) { + double difference = histogram[i] - expected_count; + chi_squared += difference * difference / expected_count; + } + // P-value for a 0.001 significance level with 7 degrees of freedom. + EXPECT_LT(chi_squared, 24.322); +} + +} // namespace blink |