Commit 5ec5ef03 authored by Thomas Guilbert's avatar Thomas Guilbert Committed by Commit Bot

Clamp video.rAF timing information

This CL decreases the resolution of the presentationTime,
expectedPresentationTime, elapsedProcessingTime and captureTime fields,
to limit potential timing attacks.

expectedPresentationTime should correspond to an animation frame
timestamp and technically shouldn't need be clamped. However, this is
not always the case in the WebRTC path, and we should keep it clamped
for now.

Bug: 1012063
Change-Id: Ie9247be8f3c91e2fef3d62cf939b1d8488f0a75a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2071113Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Reviewed-by: default avatarDaniel Vogelheim <vogelheim@chromium.org>
Commit-Queue: Thomas Guilbert <tguilbert@chromium.org>
Cr-Commit-Position: refs/heads/master@{#745612}
parent 61628b66
......@@ -13,6 +13,8 @@
#include "third_party/blink/renderer/core/dom/scripted_animation_controller.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/timing/performance.h"
#include "third_party/blink/renderer/core/timing/time_clamper.h"
#include "third_party/blink/renderer/modules/video_raf/video_frame_request_callback_collection.h"
#include "third_party/blink/renderer/platform/bindings/microtask.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
......@@ -115,16 +117,13 @@ void VideoRequestAnimationFrameImpl::ExecuteFrameCallbacks(
auto& time_converter =
GetSupplementable()->GetDocument().Loader()->GetTiming();
metadata->setPresentationTime(time_converter
.MonotonicTimeToZeroBasedDocumentTime(
frame_metadata->presentation_time)
.InMillisecondsF());
metadata->setPresentationTime(GetClampedTimeInMillis(
time_converter.MonotonicTimeToZeroBasedDocumentTime(
frame_metadata->presentation_time)));
metadata->setExpectedPresentationTime(
time_converter
.MonotonicTimeToZeroBasedDocumentTime(
frame_metadata->expected_presentation_time)
.InMillisecondsF());
metadata->setExpectedPresentationTime(GetClampedTimeInMillis(
time_converter.MonotonicTimeToZeroBasedDocumentTime(
frame_metadata->expected_presentation_time)));
metadata->setPresentedFrames(frame_metadata->presented_frames);
......@@ -137,15 +136,14 @@ void VideoRequestAnimationFrameImpl::ExecuteFrameCallbacks(
base::TimeDelta elapsed;
if (frame_metadata->metadata.GetTimeDelta(
media::VideoFrameMetadata::PROCESSING_TIME, &elapsed)) {
metadata->setElapsedProcessingTime(elapsed.InSecondsF());
metadata->setElapsedProcessingTime(GetCoarseClampedTimeInSeconds(elapsed));
}
base::TimeTicks time;
base::TimeTicks capture_time;
if (frame_metadata->metadata.GetTimeTicks(
media::VideoFrameMetadata::CAPTURE_BEGIN_TIME, &time)) {
metadata->setCaptureTime(
time_converter.MonotonicTimeToZeroBasedDocumentTime(time)
.InMillisecondsF());
media::VideoFrameMetadata::CAPTURE_BEGIN_TIME, &capture_time)) {
metadata->setCaptureTime(GetClampedTimeInMillis(
time_converter.MonotonicTimeToZeroBasedDocumentTime(capture_time)));
}
double rtp_timestamp;
......@@ -160,6 +158,28 @@ void VideoRequestAnimationFrameImpl::ExecuteFrameCallbacks(
pending_execution_ = false;
}
// static
double VideoRequestAnimationFrameImpl::GetClampedTimeInMillis(
base::TimeDelta time) {
constexpr double kSecondsToMillis = 1000.0;
return Performance::ClampTimeResolution(time.InSecondsF()) * kSecondsToMillis;
}
// static
double VideoRequestAnimationFrameImpl::GetCoarseClampedTimeInSeconds(
base::TimeDelta time) {
constexpr double kCoarseResolutionInSeconds = 100e-6;
// Add this assert, in case TimeClamper's resolution were to change to be
// stricter.
static_assert(kCoarseResolutionInSeconds >= TimeClamper::kResolutionSeconds,
"kCoarseResolutionInSeconds should be at least "
"as coarse as other clock resolutions");
double interval = floor(time.InSecondsF() / kCoarseResolutionInSeconds);
double clamped_time = interval * kCoarseResolutionInSeconds;
return clamped_time;
}
int VideoRequestAnimationFrameImpl::requestAnimationFrame(
V8VideoFrameRequestCallback* callback) {
if (auto* player = GetSupplementable()->GetWebMediaPlayer())
......
......@@ -51,6 +51,11 @@ class MODULES_EXPORT VideoRequestAnimationFrameImpl final
void RegisterCallbackForTest(
VideoFrameRequestCallbackCollection::VideoFrameCallback*);
// Utility functions to limit the clock resolution of fields, for security
// reasons.
static double GetClampedTimeInMillis(base::TimeDelta time);
static double GetCoarseClampedTimeInSeconds(base::TimeDelta time);
// Used to keep track of whether or not we have already scheduled a call to
// ExecuteFrameCallbacks() in the next rendering steps.
bool pending_execution_ = false;
......
......@@ -13,6 +13,7 @@
#include "third_party/blink/renderer/core/html/media/html_video_element.h"
#include "third_party/blink/renderer/core/loader/empty_clients.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/core/timing/performance.h"
#include "third_party/blink/renderer/platform/testing/empty_web_media_player.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
......@@ -82,19 +83,25 @@ class MetadataHelper {
if (initialized)
return;
// We don't want any time ticks be a multiple of 5us, otherwise, we couldn't
// tell whether or not the implementation clamped their values. Therefore,
// we manually set the values for a deterministic test, and make sure we
// have sub-microsecond resolution for those values.
base::TimeTicks now = base::TimeTicks::Now();
metadata_.presented_frames = 42;
metadata_.presentation_time = now + base::TimeDelta::FromMilliseconds(10);
metadata_.presentation_time =
now + base::TimeDelta::FromMillisecondsD(10.1234);
metadata_.expected_presentation_time =
now + base::TimeDelta::FromMilliseconds(26);
now + base::TimeDelta::FromMillisecondsD(26.3467);
metadata_.width = 320;
metadata_.height = 480;
metadata_.presentation_timestamp = base::TimeDelta::FromSecondsD(3.14);
metadata_.metadata.SetTimeDelta(media::VideoFrameMetadata::PROCESSING_TIME,
base::TimeDelta::FromMilliseconds(60));
base::TimeDelta::FromMillisecondsD(60.982));
metadata_.metadata.SetTimeTicks(
media::VideoFrameMetadata::CAPTURE_BEGIN_TIME,
now + base::TimeDelta::FromMilliseconds(5));
now + base::TimeDelta::FromMillisecondsD(5.6785));
metadata_.metadata.SetDouble(media::VideoFrameMetadata::RTP_TIMESTAMP,
12345);
......@@ -123,40 +130,70 @@ class VideoRafParameterVerifierCallback
auto* expected = MetadataHelper::GetDefaultMedatada();
EXPECT_EQ(expected->presented_frames, metadata->presentedFrames());
EXPECT_EQ(TicksToMillisecondsF(expected->presentation_time),
metadata->presentationTime());
EXPECT_EQ(TicksToMillisecondsF(expected->expected_presentation_time),
metadata->expectedPresentationTime());
EXPECT_EQ((unsigned int)expected->width, metadata->width());
EXPECT_EQ((unsigned int)expected->height, metadata->height());
EXPECT_EQ(expected->presentation_timestamp.InSecondsF(),
metadata->presentationTimestamp());
base::TimeDelta processing_time;
EXPECT_TRUE(expected->metadata.GetTimeDelta(
media::VideoFrameMetadata::PROCESSING_TIME, &processing_time));
EXPECT_EQ(processing_time.InSecondsF(), metadata->elapsedProcessingTime());
double rtp_timestamp;
EXPECT_TRUE(expected->metadata.GetDouble(
media::VideoFrameMetadata::RTP_TIMESTAMP, &rtp_timestamp));
EXPECT_EQ(rtp_timestamp, metadata->rtpTimestamp());
// Verify that values were correctly clamped.
VerifyTicksClamping(expected->presentation_time,
metadata->presentationTime(), "presentation_time");
VerifyTicksClamping(expected->expected_presentation_time,
metadata->expectedPresentationTime(),
"expected_presentation_time");
base::TimeTicks capture_time;
EXPECT_TRUE(expected->metadata.GetTimeTicks(
media::VideoFrameMetadata::CAPTURE_BEGIN_TIME, &capture_time));
EXPECT_EQ(TicksToMillisecondsF(capture_time), metadata->captureTime());
VerifyTicksClamping(capture_time, metadata->captureTime(), "capture_time");
double rtp_timestamp;
EXPECT_TRUE(expected->metadata.GetDouble(
media::VideoFrameMetadata::RTP_TIMESTAMP, &rtp_timestamp));
EXPECT_EQ(rtp_timestamp, metadata->rtpTimestamp());
base::TimeDelta processing_time;
EXPECT_TRUE(expected->metadata.GetTimeDelta(
media::VideoFrameMetadata::PROCESSING_TIME, &processing_time));
EXPECT_EQ(ClampElapsedProcessingTime(processing_time),
metadata->elapsedProcessingTime());
EXPECT_NE(processing_time.InSecondsF(), metadata->elapsedProcessingTime());
}
double last_now() { return now_; }
bool was_invoked() { return was_invoked_; }
private:
void VerifyTicksClamping(base::TimeTicks reference,
double actual,
std::string name) {
EXPECT_EQ(TicksToClampedMillisecondsF(reference), actual)
<< name << " was not clamped properly.";
EXPECT_NE(TicksToMillisecondsF(reference), actual)
<< "Did not successfully test clamping for " << name;
}
double TicksToClampedMillisecondsF(base::TimeTicks ticks) {
constexpr double kSecondsToMillis = 1000.0;
return Performance::ClampTimeResolution(
timing_.MonotonicTimeToZeroBasedDocumentTime(ticks)
.InSecondsF()) *
kSecondsToMillis;
}
double TicksToMillisecondsF(base::TimeTicks ticks) {
return timing_.MonotonicTimeToZeroBasedDocumentTime(ticks)
.InMillisecondsF();
}
static double ClampElapsedProcessingTime(base::TimeDelta time) {
constexpr double kProcessingTimeResolution = 100e-6;
double interval = floor(time.InSecondsF() / kProcessingTimeResolution);
double clamped_time = interval * kProcessingTimeResolution;
return clamped_time;
}
double now_;
bool was_invoked_ = false;
DocumentLoadTiming& timing_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment