Chrome · XR
CVE-2026-79125
Logic Error in XR
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifcontent/browser/xr/service/vr_service_impl.cc |
modified |
Files Changed
content/browser/xr/service/vr_service_impl.cccontent/browser/xr/service/vr_service_impl.h
Patch
From 86ca49b1d3680f6c9c60ab20d59440b3810df1f8 Mon Sep 17 00:00:00 2001
From: Alexander Cooper <alcooper@chromium.org>
Date: Mon, 13 Jul 2026 11:08:52 -0700
Subject: [PATCH] Gate VRService RuntimesChanged on frame visibility
VRServiceImpl previously forwarded XR runtime hot-plug events via
RuntimesChanged() without checking if the associated RenderFrameHost
was visible.
To align with other platform sensors (e.g., DevicePostureProviderImpl)
and ensure events are only delivered to visible frames, this change:
- Adds an IsRenderFrameHostVisible() helper method to VRServiceImpl.
- Checks frame visibility in RuntimesChanged() and sets a
pending_device_changed_ flag if the frame is not currently visible.
- Overrides WebContentsObserver::OnVisibilityChanged() to flush any
deferred device change notifications once the associated frame
becomes visible again.
TAG=agy
Fixed: 533001362
Change-Id: I405b72ce431b100eed229996c48911cce192a617
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8077558
Reviewed-by: Brandon Jones <bajones@chromium.org>
Commit-Queue: Alexander Cooper <alcooper@chromium.org>
Commit-Queue: Brandon Jones <bajones@chromium.org>
Auto-Submit: Alexander Cooper <alcooper@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1661243}
---
diff --git a/content/browser/xr/service/vr_service_impl.cc b/content/browser/xr/service/vr_service_impl.cc
index 2fde838f..8f3cf7f 100644
--- a/content/browser/xr/service/vr_service_impl.cc
+++ b/content/browser/xr/service/vr_service_impl.cc
@@ -274,6 +274,10 @@
void VRServiceImpl::RuntimesChanged() {
DVLOG(2) << __func__;
+ if (!IsRenderFrameHostVisible()) {
+ pending_device_changed_ = true;
+ return;
+ }
if (service_client_) {
service_client_->OnDeviceChanged();
}
@@ -303,6 +307,18 @@
receiver_->Close();
}
+void VRServiceImpl::OnVisibilityChanged(content::Visibility visibility) {
+ // Re-check frame visibility, as our associated RenderFrameHost may remain
+ // hidden even when the top-level page becomes visible.
+ if (!pending_device_changed_ || !IsRenderFrameHostVisible()) {
+ return;
+ }
+ pending_device_changed_ = false;
+ if (service_client_) {
+ service_client_->OnDeviceChanged();
+ }
+}
+
void VRServiceImpl::OnWebContentsFocusChanged(content::RenderWidgetHost* host,
bool focused) {
if (!render_frame_host_ || !render_frame_host_->GetView() ||
@@ -550,8 +566,7 @@
return;
}
- if (render_frame_host_->GetVisibilityState() !=
- content::PageVisibilityState::kVisible) {
+ if (!IsRenderFrameHostVisible()) {
// Page visibility is verified blink-side, so this should never fail unless
// the requesting client is misbehaving or compromised. Treat non-visible
// page as unknown failure:
@@ -997,6 +1012,11 @@
return content::WebContents::FromRenderFrameHost(render_frame_host_);
}
+bool VRServiceImpl::IsRenderFrameHostVisible() const {
+ return render_frame_host_ && render_frame_host_->GetVisibilityState() ==
+ content::PageVisibilityState::kVisible;
+}
+
void VRServiceImpl::Teardown() {
if (!render_frame_host_) {
return;
diff --git a/content/browser/xr/service/vr_service_impl.h b/content/browser/xr/service/vr_service_impl.h
index 65bd2a02..f36d92b 100644
--- a/content/browser/xr/service/vr_service_impl.h
+++ b/content/browser/xr/service/vr_service_impl.h
@@ -14,6 +14,7 @@
#include "content/browser/xr/metrics/session_metrics_helper.h"
#include "content/common/content_export.h"
#include "content/public/browser/permission_result.h"
+#include "content/public/browser/visibility.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/xr_install_helper.h"
#include "device/vr/public/mojom/isolated_xr_service.mojom-forward.h"
@@ -126,6 +127,7 @@
void OnWebContentsFocused(content::RenderWidgetHost* host) override;
void OnWebContentsLostFocus(content::RenderWidgetHost* host) override;
void RenderFrameDeleted(content::RenderFrameHost* host) override;
+ void OnVisibilityChanged(content::Visibility visibility) override;
void OnWebContentsFocusChanged(content::RenderWidgetHost* host, bool focused);
@@ -137,6 +139,8 @@
// assumption that we are not already in VR.
SessionMetricsHelper* GetSessionMetricsHelper();
+ bool IsRenderFrameHostVisible() const;
+
bool InternalSupportsSession(device::mojom::XRSessionOptions* options);
void DoRequestPermissions(
@@ -206,6 +210,7 @@
bool in_focused_frame_ = false;
bool frames_throttled_ = false;
bool has_immersive_session_ = false;
+ bool pending_device_changed_ = false;
std::vector<XrCompatibleCallback> xr_compatible_callbacks_;
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page