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 10:24:59 +0000 |
commit | 17e4aafb6fe894047b46abeb3c3b8290de4094cf (patch) | |
tree | 2ed9ffd73e57954896a20e7c6fd3565b469d0992 | |
parent | 44b6c003b1e3ab8ef4ce9d516fd24d7a8eefb2ff (diff) | |
download | qtwebengine-chromium-17e4aafb6fe894047b46abeb3c3b8290de4094cf.tar.gz |
[Backport] Clamp performance.now() to 100us.
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>
10 files changed, 209 insertions, 11 deletions
diff --git a/chromium/third_party/WebKit/Source/core/dom/IdleDeadline.cpp b/chromium/third_party/WebKit/Source/core/dom/IdleDeadline.cpp index df981b7b097..415dce1b67e 100644 --- a/chromium/third_party/WebKit/Source/core/dom/IdleDeadline.cpp +++ b/chromium/third_party/WebKit/Source/core/dom/IdleDeadline.cpp @@ -17,12 +17,12 @@ IdleDeadline::IdleDeadline(double deadline_seconds, CallbackType callback_type) double IdleDeadline::timeRemaining() const { double time_remaining = deadline_seconds_ - MonotonicallyIncreasingTime(); if (time_remaining < 0) { - time_remaining = 0; + return 0; } else if (Platform::Current() ->CurrentThread() ->Scheduler() ->ShouldYieldForHighPriorityWork()) { - time_remaining = 0; + return 0; } return 1000.0 * PerformanceBase::ClampTimeResolution(time_remaining); diff --git a/chromium/third_party/WebKit/Source/core/dom/IdleDeadlineTest.cpp b/chromium/third_party/WebKit/Source/core/dom/IdleDeadlineTest.cpp index a9cd8887e01..c215904b594 100644 --- a/chromium/third_party/WebKit/Source/core/dom/IdleDeadlineTest.cpp +++ b/chromium/third_party/WebKit/Source/core/dom/IdleDeadlineTest.cpp @@ -86,7 +86,7 @@ TEST_F(IdleDeadlineTest, deadlineInFuture) { IdleDeadline* deadline = IdleDeadline::Create(1.25, IdleDeadline::CallbackType::kCalledWhenIdle); // Note: the deadline is computed with reduced resolution. - EXPECT_FLOAT_EQ(249.995, deadline->timeRemaining()); + EXPECT_FLOAT_EQ(250.0, deadline->timeRemaining()); } TEST_F(IdleDeadlineTest, deadlineInPast) { diff --git a/chromium/third_party/WebKit/Source/core/dom/ScriptedIdleTaskController.cpp b/chromium/third_party/WebKit/Source/core/dom/ScriptedIdleTaskController.cpp index 1b7c3b1032c..3d8c03f5b72 100644 --- a/chromium/third_party/WebKit/Source/core/dom/ScriptedIdleTaskController.cpp +++ b/chromium/third_party/WebKit/Source/core/dom/ScriptedIdleTaskController.cpp @@ -35,7 +35,6 @@ class IdleRequestCallbackWrapper static void IdleTaskFired( PassRefPtr<IdleRequestCallbackWrapper> callback_wrapper, double deadline_seconds) { - // TODO(rmcilroy): Implement clamping of deadline in some form. if (ScriptedIdleTaskController* controller = callback_wrapper->Controller()) { // If we are going to yield immediately, reschedule the callback for diff --git a/chromium/third_party/WebKit/Source/core/timing/PerformanceBase.cpp b/chromium/third_party/WebKit/Source/core/timing/PerformanceBase.cpp index 90c794e0061..5f14cffb213 100644 --- a/chromium/third_party/WebKit/Source/core/timing/PerformanceBase.cpp +++ b/chromium/third_party/WebKit/Source/core/timing/PerformanceBase.cpp @@ -44,6 +44,7 @@ #include "core/timing/PerformanceResourceTiming.h" #include "core/timing/PerformanceUserTiming.h" #include "platform/RuntimeEnabledFeatures.h" +#include "platform/TimeClamper.h" #include "platform/loader/fetch/ResourceResponse.h" #include "platform/loader/fetch/ResourceTimingInfo.h" #include "platform/weborigin/SecurityOrigin.h" @@ -508,8 +509,8 @@ void PerformanceBase::DeliverObservationsTimerFired(TimerBase*) { // static double PerformanceBase::ClampTimeResolution(double time_seconds) { - const double kResolutionSeconds = 0.000005; - return floor(time_seconds / kResolutionSeconds) * kResolutionSeconds; + DEFINE_THREAD_SAFE_STATIC_LOCAL(TimeClamper, clamper, ()); + return clamper.ClampTimeResolution(time_seconds); } // static @@ -521,11 +522,11 @@ DOMHighResTimeStamp PerformanceBase::MonotonicTimeToDOMHighResTimeStamp( if (!monotonic_time || !time_origin) return 0.0; - double time_in_seconds = monotonic_time - time_origin; - if (time_in_seconds < 0 && !allow_negative_value) + double clamped_time_in_seconds = + ClampTimeResolution(monotonic_time) - ClampTimeResolution(time_origin); + if (clamped_time_in_seconds < 0 && !allow_negative_value) return 0.0; - return ConvertSecondsToDOMHighResTimeStamp( - ClampTimeResolution(time_in_seconds)); + return ConvertSecondsToDOMHighResTimeStamp(clamped_time_in_seconds); } DOMHighResTimeStamp PerformanceBase::MonotonicTimeToDOMHighResTimeStamp( diff --git a/chromium/third_party/WebKit/Source/core/timing/PerformanceBase.h b/chromium/third_party/WebKit/Source/core/timing/PerformanceBase.h index f94a11ad882..625a45c49c1 100644 --- a/chromium/third_party/WebKit/Source/core/timing/PerformanceBase.h +++ b/chromium/third_party/WebKit/Source/core/timing/PerformanceBase.h @@ -70,7 +70,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 time_seconds); diff --git a/chromium/third_party/WebKit/Source/platform/BUILD.gn b/chromium/third_party/WebKit/Source/platform/BUILD.gn index 85adfaf2098..5ba4275c714 100644 --- a/chromium/third_party/WebKit/Source/platform/BUILD.gn +++ b/chromium/third_party/WebKit/Source/platform/BUILD.gn @@ -320,6 +320,8 @@ component("platform") { "Theme.cpp", "Theme.h", "ThemeTypes.h", + "TimeClamper.cpp", + "TimeClamper.h", "Timer.cpp", "Timer.h", "UUID.cpp", @@ -1824,6 +1826,7 @@ test("blink_platform_unittests") { "PODRedBlackTreeTest.cpp", "ScopedOrientationChangeIndicatorTest.cpp", "SharedBufferTest.cpp", + "TimeClamperTest.cpp", "TimerTest.cpp", "UUIDTest.cpp", "WebIconSizesParserTest.cpp", diff --git a/chromium/third_party/WebKit/Source/platform/DEPS b/chromium/third_party/WebKit/Source/platform/DEPS index b31a2ecbcd6..b026f2f9ff9 100644 --- a/chromium/third_party/WebKit/Source/platform/DEPS +++ b/chromium/third_party/WebKit/Source/platform/DEPS @@ -6,6 +6,7 @@ include_rules = [ "+base/bind_helpers.h", "+base/callback.h", "+base/callback_forward.h", + "+base/bit_cast.h", "+base/feature_list.h", "+base/files", "+base/guid.h", 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..18244e769f3 --- /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 "platform/wtf/Assertions.h" +#include "platform/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 |