Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(245)

Side by Side Diff: chrome/browser/android/vr_shell/vr_shell_gl.cc

Issue 2950233002: Validate untrusted VR mojo inputs into browser process (Closed)
Patch Set: make validation less strict, and fix a bug found with validation Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/android/vr_shell/vr_shell_gl.h" 5 #include "chrome/browser/android/vr_shell/vr_shell_gl.h"
6 6
7 #include <chrono> 7 #include <chrono>
8 #include <limits> 8 #include <limits>
9 #include <utility> 9 #include <utility>
10 10
(...skipping 360 matching lines...) Expand 10 before | Expand all | Expand 10 after
371 const gpu::MailboxHolder& mailbox) { 371 const gpu::MailboxHolder& mailbox) {
372 TRACE_EVENT0("gpu", "VrShellGl::SubmitWebVRFrame"); 372 TRACE_EVENT0("gpu", "VrShellGl::SubmitWebVRFrame");
373 373
374 // submit_client_ could be null when we exit presentation, if there were 374 // submit_client_ could be null when we exit presentation, if there were
375 // pending SubmitFrame messages queued. VRDisplayClient::OnExitPresent 375 // pending SubmitFrame messages queued. VRDisplayClient::OnExitPresent
376 // will clean up state in blink, so it doesn't wait for 376 // will clean up state in blink, so it doesn't wait for
377 // OnSubmitFrameTransferred or OnSubmitFrameRendered. 377 // OnSubmitFrameTransferred or OnSubmitFrameRendered.
378 if (!submit_client_.get()) 378 if (!submit_client_.get())
379 return; 379 return;
380 380
381 if (frame_index < 0 ||
382 !webvr_frame_oustanding_[frame_index % kPoseRingBufferSize]) {
383 mojo::ReportBadMessage("SubmitFrame called with an invalid frame_index");
384 binding_.Close();
385 return;
386 }
387
381 webvr_time_js_submit_[frame_index % kPoseRingBufferSize] = 388 webvr_time_js_submit_[frame_index % kPoseRingBufferSize] =
382 base::TimeTicks::Now(); 389 base::TimeTicks::Now();
383 390
384 // Swapping twice on a Surface without calling updateTexImage in 391 // Swapping twice on a Surface without calling updateTexImage in
385 // between can lose frames, so don't draw+swap if we already have 392 // between can lose frames, so don't draw+swap if we already have
386 // a pending frame we haven't consumed yet. 393 // a pending frame we haven't consumed yet.
387 bool swapped = false; 394 bool swapped = false;
388 if (pending_frames_.empty()) { 395 if (pending_frames_.empty()) {
389 swapped = mailbox_bridge_->CopyMailboxToSurfaceAndSwap(mailbox); 396 swapped = mailbox_bridge_->CopyMailboxToSurfaceAndSwap(mailbox);
390 if (swapped) { 397 if (swapped) {
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
448 browser_->ToggleCardboardGamepad(true); 455 browser_->ToggleCardboardGamepad(true);
449 } 456 }
450 } 457 }
451 458
452 void VrShellGl::InitializeRenderer() { 459 void VrShellGl::InitializeRenderer() {
453 gvr_api_->InitializeGl(); 460 gvr_api_->InitializeGl();
454 gfx::Transform head_pose; 461 gfx::Transform head_pose;
455 device::GvrDelegate::GetGvrPoseWithNeckModel(gvr_api_.get(), &head_pose); 462 device::GvrDelegate::GetGvrPoseWithNeckModel(gvr_api_.get(), &head_pose);
456 webvr_head_pose_.assign(kPoseRingBufferSize, head_pose); 463 webvr_head_pose_.assign(kPoseRingBufferSize, head_pose);
457 webvr_time_pose_.assign(kPoseRingBufferSize, base::TimeTicks()); 464 webvr_time_pose_.assign(kPoseRingBufferSize, base::TimeTicks());
465 webvr_frame_oustanding_.assign(kPoseRingBufferSize, false);
458 webvr_time_js_submit_.assign(kPoseRingBufferSize, base::TimeTicks()); 466 webvr_time_js_submit_.assign(kPoseRingBufferSize, base::TimeTicks());
459 467
460 std::vector<gvr::BufferSpec> specs; 468 std::vector<gvr::BufferSpec> specs;
461 // For kFramePrimaryBuffer (primary VrShell and WebVR content) 469 // For kFramePrimaryBuffer (primary VrShell and WebVR content)
462 specs.push_back(gvr_api_->CreateBufferSpec()); 470 specs.push_back(gvr_api_->CreateBufferSpec());
463 gvr::Sizei render_size_primary = specs[kFramePrimaryBuffer].GetSize(); 471 gvr::Sizei render_size_primary = specs[kFramePrimaryBuffer].GetSize();
464 render_size_primary_ = {render_size_primary.width, 472 render_size_primary_ = {render_size_primary.width,
465 render_size_primary.height}; 473 render_size_primary.height};
466 render_size_vrshell_ = render_size_primary_; 474 render_size_vrshell_ = render_size_primary_;
467 475
(...skipping 592 matching lines...) Expand 10 before | Expand all | Expand 10 after
1060 1068
1061 // When using async reprojection, we need to know which pose was 1069 // When using async reprojection, we need to know which pose was
1062 // used in the WebVR app for drawing this frame and supply it when 1070 // used in the WebVR app for drawing this frame and supply it when
1063 // submitting. Technically we don't need a pose if not reprojecting, 1071 // submitting. Technically we don't need a pose if not reprojecting,
1064 // but keeping it uninitialized seems likely to cause problems down 1072 // but keeping it uninitialized seems likely to cause problems down
1065 // the road. Copying it is cheaper than fetching a new one. 1073 // the road. Copying it is cheaper than fetching a new one.
1066 if (ShouldDrawWebVr()) { 1074 if (ShouldDrawWebVr()) {
1067 static_assert(!((kPoseRingBufferSize - 1) & kPoseRingBufferSize), 1075 static_assert(!((kPoseRingBufferSize - 1) & kPoseRingBufferSize),
1068 "kPoseRingBufferSize must be a power of 2"); 1076 "kPoseRingBufferSize must be a power of 2");
1069 head_pose = webvr_head_pose_[frame_index % kPoseRingBufferSize]; 1077 head_pose = webvr_head_pose_[frame_index % kPoseRingBufferSize];
1078 webvr_frame_oustanding_[frame_index % kPoseRingBufferSize] = false;
1070 } else { 1079 } else {
1071 device::GvrDelegate::GetGvrPoseWithNeckModel(gvr_api_.get(), &head_pose); 1080 device::GvrDelegate::GetGvrPoseWithNeckModel(gvr_api_.get(), &head_pose);
1072 } 1081 }
1073 1082
1074 // Update the render position of all UI elements (including desktop). 1083 // Update the render position of all UI elements (including desktop).
1075 scene_->OnBeginFrame(current_time); 1084 scene_->OnBeginFrame(current_time);
1076 1085
1077 { 1086 {
1078 // TODO(crbug.com/704690): Acquire controller state in a way that's timely 1087 // TODO(crbug.com/704690): Acquire controller state in a way that's timely
1079 // for both the gamepad API and UI input handling. 1088 // for both the gamepad API and UI input handling.
(...skipping 490 matching lines...) Expand 10 before | Expand all | Expand 10 after
1570 vsync_timebase_ += base::TimeDelta::FromMicroseconds(timebase_nanos / 1000); 1579 vsync_timebase_ += base::TimeDelta::FromMicroseconds(timebase_nanos / 1000);
1571 vsync_interval_ = base::TimeDelta::FromSecondsD(interval_seconds); 1580 vsync_interval_ = base::TimeDelta::FromSecondsD(interval_seconds);
1572 vsync_task_.Reset(base::Bind(&VrShellGl::OnVSync, base::Unretained(this))); 1581 vsync_task_.Reset(base::Bind(&VrShellGl::OnVSync, base::Unretained(this)));
1573 OnVSync(); 1582 OnVSync();
1574 } 1583 }
1575 1584
1576 void VrShellGl::ForceExitVr() { 1585 void VrShellGl::ForceExitVr() {
1577 browser_->ForceExitVr(); 1586 browser_->ForceExitVr();
1578 } 1587 }
1579 1588
1589 namespace {
1590 bool ValidateRect(const gfx::RectF& bounds) {
1591 // Bounds should be between 0 and 1, with positive width/height.
1592 // We simply clamp to [0,1], but still validate that the bounds are not NAN.
1593 return !std::isnan(bounds.width()) && !std::isnan(bounds.height()) &&
1594 !std::isnan(bounds.x()) && !std::isnan(bounds.y());
1595 }
1596
1597 bool ValidateSize(const gfx::Size& size) {
1598 // Size should be at least 0 in each direction.
1599 return size.width() >= 0 && size.height() >= 0;
1600 }
1601
1602 gfx::RectF ClampRect(const gfx::RectF& bounds) {
1603 float left = std::max(0.0f, std::min(bounds.x(), 1.0f));
mthiesse 2017/07/11 14:52:24 I think you can just use bounds.AdjustToFit(gfx::R
billorr 2017/07/13 00:31:40 That is close enough to the same behavior that I'l
1604 float right = std::max(left, std::min(bounds.right(), 1.0f));
1605 float top = std::max(0.0f, std::min(bounds.y(), 1.0f));
1606 float bottom = std::max(top, std::min(bounds.bottom(), 1.0f));
1607 return gfx::RectF(left, top, right - left, bottom - top);
bajones1 2017/07/11 17:11:06 Sanity check: Does gfx::RectF enforces that right
billorr 2017/07/13 00:31:41 Yes, setWidth/setHeight will make the width/height
1608 }
1609
1610 } // namespace
1611
1580 void VrShellGl::UpdateLayerBounds(int16_t frame_index, 1612 void VrShellGl::UpdateLayerBounds(int16_t frame_index,
1581 const gfx::RectF& left_bounds, 1613 const gfx::RectF& left_bounds,
1582 const gfx::RectF& right_bounds, 1614 const gfx::RectF& right_bounds,
1583 const gfx::Size& source_size) { 1615 const gfx::Size& source_size) {
1616 if (!ValidateSize(source_size) || !ValidateRect(left_bounds) ||
1617 !ValidateRect(right_bounds)) {
1618 mojo::ReportBadMessage("UpdateLayerBounds called with invalid bounds");
1619 binding_.Close();
1620 return;
1621 }
1622
1623 if (frame_index >= 0 &&
1624 !webvr_frame_oustanding_[frame_index % kPoseRingBufferSize]) {
1625 mojo::ReportBadMessage("UpdateLayerBounds called with invalid frame_index");
1626 binding_.Close();
1627 return;
1628 }
1629
1584 if (frame_index < 0) { 1630 if (frame_index < 0) {
1585 webvr_left_viewport_->SetSourceUv(UVFromGfxRect(left_bounds)); 1631 webvr_left_viewport_->SetSourceUv(UVFromGfxRect(ClampRect(left_bounds)));
1586 webvr_right_viewport_->SetSourceUv(UVFromGfxRect(right_bounds)); 1632 webvr_right_viewport_->SetSourceUv(UVFromGfxRect(ClampRect(right_bounds)));
1587 CreateOrResizeWebVRSurface(source_size); 1633 CreateOrResizeWebVRSurface(source_size);
1634
1635 // clear all pending bounds
1636 pending_bounds_ = std::queue<std::pair<uint8_t, WebVrBounds>>();
1588 } else { 1637 } else {
1589 pending_bounds_.emplace( 1638 pending_bounds_.emplace(
1590 frame_index, WebVrBounds(left_bounds, right_bounds, source_size)); 1639 frame_index, WebVrBounds(left_bounds, right_bounds, source_size));
1591 } 1640 }
1592 } 1641 }
1593 1642
1594 int64_t VrShellGl::GetPredictedFrameTimeNanos() { 1643 int64_t VrShellGl::GetPredictedFrameTimeNanos() {
1595 int64_t frame_time_micros = vsync_interval_.InMicroseconds(); 1644 int64_t frame_time_micros = vsync_interval_.InMicroseconds();
1596 // If we aim to submit at vsync, that frame will start scanning out 1645 // If we aim to submit at vsync, that frame will start scanning out
1597 // one vsync later. Add a half frame to split the difference between 1646 // one vsync later. Add a half frame to split the difference between
(...skipping 16 matching lines...) Expand all
1614 TRACE_EVENT1("input", "VrShellGl::SendVSync", "frame", frame_index); 1663 TRACE_EVENT1("input", "VrShellGl::SendVSync", "frame", frame_index);
1615 1664
1616 int64_t prediction_nanos = GetPredictedFrameTimeNanos(); 1665 int64_t prediction_nanos = GetPredictedFrameTimeNanos();
1617 1666
1618 gfx::Transform head_mat; 1667 gfx::Transform head_mat;
1619 device::mojom::VRPosePtr pose = 1668 device::mojom::VRPosePtr pose =
1620 device::GvrDelegate::GetVRPosePtrWithNeckModel(gvr_api_.get(), &head_mat, 1669 device::GvrDelegate::GetVRPosePtrWithNeckModel(gvr_api_.get(), &head_mat,
1621 prediction_nanos); 1670 prediction_nanos);
1622 1671
1623 webvr_head_pose_[frame_index % kPoseRingBufferSize] = head_mat; 1672 webvr_head_pose_[frame_index % kPoseRingBufferSize] = head_mat;
1673 webvr_frame_oustanding_[frame_index % kPoseRingBufferSize] = true;
1624 webvr_time_pose_[frame_index % kPoseRingBufferSize] = base::TimeTicks::Now(); 1674 webvr_time_pose_[frame_index % kPoseRingBufferSize] = base::TimeTicks::Now();
1625 1675
1626 std::move(callback).Run( 1676 std::move(callback).Run(
1627 std::move(pose), time, frame_index, 1677 std::move(pose), time, frame_index,
1628 device::mojom::VRPresentationProvider::VSyncStatus::SUCCESS); 1678 device::mojom::VRPresentationProvider::VSyncStatus::SUCCESS);
1629 } 1679 }
1630 1680
1631 void VrShellGl::CreateVRDisplayInfo( 1681 void VrShellGl::CreateVRDisplayInfo(
1632 const base::Callback<void(device::mojom::VRDisplayInfoPtr)>& callback, 1682 const base::Callback<void(device::mojom::VRDisplayInfoPtr)>& callback,
1633 uint32_t device_id) { 1683 uint32_t device_id) {
1634 // This assumes that the initial webvr_surface_size_ was set to the 1684 // This assumes that the initial webvr_surface_size_ was set to the
1635 // appropriate recommended render resolution as the default size during 1685 // appropriate recommended render resolution as the default size during
1636 // InitializeGl. Revisit if the initialization order changes. 1686 // InitializeGl. Revisit if the initialization order changes.
1637 device::mojom::VRDisplayInfoPtr info = 1687 device::mojom::VRDisplayInfoPtr info =
1638 device::GvrDelegate::CreateVRDisplayInfo(gvr_api_.get(), 1688 device::GvrDelegate::CreateVRDisplayInfo(gvr_api_.get(),
1639 webvr_surface_size_, device_id); 1689 webvr_surface_size_, device_id);
1640 browser_->RunVRDisplayInfoCallback(callback, &info); 1690 browser_->RunVRDisplayInfoCallback(callback, &info);
1641 } 1691 }
1642 1692
1643 } // namespace vr_shell 1693 } // namespace vr_shell
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698