Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient policy enforcement in WebXR
DescriptionInsufficient policy enforcement in WebXR
ComponentWebXR
Bug ClassLogic Error
Tracker507231605
Fix commit36f6ba524785 (chromium/src) +22/-7
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Changed Functions

FunctionChangeNotes
if
third_party/blink/renderer/modules/xr/xr_input_source.cc
modified

Files Changed

  • third_party/blink/renderer/modules/xr/xr_input_source.cc
From 36f6ba524785efa77c93229a73e68f18846109b1 Mon Sep 17 00:00:00 2001
From: Alexander Cooper <alcooper@chromium.org>
Date: Mon, 11 May 2026 14:03:39 -0700
Subject: [PATCH] [WebXR] Improve input suppression for cross-origin DOM Overlay

Modify the DOM Overlay hit testing logic to correctly identify and
suppress input events when they intersect cross-origin frames.

Specifically, we now use the frame element's content frame directly
instead of checking for the content document. This allows properly
recognizing and handling Out-of-Process Iframes (RemoteFrames) which do
not have a local content document.

Additionally, we compare the hit frame's security origin against the
origin of the WebXR session's execution context, rather than comparing
against the outermost main frame. This correctly handles cases where the
XR session itself is running in an embedded subframe.

Fixed: 507231605
Change-Id: Ie4d5291c36f86c16d5cd8f78fc0a08a1ce783096
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7837042
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Auto-Submit: Alexander Cooper <alcooper@chromium.org>
Commit-Queue: Alexander Cooper <alcooper@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1628794}
---

diff --git a/third_party/blink/renderer/modules/xr/xr_input_source.cc b/third_party/blink/renderer/modules/xr/xr_input_source.cc
index a186bb7..e3f08c59 100644
--- a/third_party/blink/renderer/modules/xr/xr_input_source.cc
+++ b/third_party/blink/renderer/modules/xr/xr_input_source.cc
@@ -12,6 +12,8 @@
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/events/event_dispatcher.h"
 #include "third_party/blink/renderer/core/dom/events/event_path.h"
+#include "third_party/blink/renderer/core/execution_context/security_context.h"
+#include "third_party/blink/renderer/core/frame/frame.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/html/html_frame_element_base.h"
@@ -26,6 +28,7 @@
 #include "third_party/blink/renderer/modules/xr/xr_system.h"
 #include "third_party/blink/renderer/modules/xr/xr_target_ray_space.h"
 #include "third_party/blink/renderer/modules/xr/xr_utils.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace blink {
 
@@ -519,13 +522,25 @@
   // the common base class to cover both. (There's no intention to actively
   // support framesets for DOM Overlay, but this helps prevent them from
   // being used as a mechanism for information leaks.)
-  HTMLFrameElementBase* frame = DynamicTo<HTMLFrameElementBase>(hit_element);
-  if (frame) {
-    Document* hit_document = frame->contentDocument();
-    if (hit_document) {
-      Frame* hit_frame = hit_document->GetFrame();
-      DCHECK(hit_frame);
-      if (hit_frame->IsCrossOriginToOutermostMainFrame()) {
+  HTMLFrameElementBase* frame_element =
+      DynamicTo<HTMLFrameElementBase>(hit_element);
+  if (frame_element) {
+    Frame* hit_frame = frame_element->ContentFrame();
+    if (hit_frame) {
+      bool is_cross_origin = false;
+      if (hit_frame->IsRemoteFrame()) {
+        is_cross_origin = true;
+      } else {
+        const SecurityOrigin* hit_origin =
+            hit_frame->GetSecurityContext()->GetSecurityOrigin();
+        const SecurityOrigin* session_origin =
+            session_->GetExecutionContext()->GetSecurityOrigin();
+        if (!hit_origin->IsSameOriginWith(session_origin)) {
+          is_cross_origin = true;
+        }
+      }
+
+      if (is_cross_origin) {
         // Mark the input source as invisible until the primary button is
         // released.
         state_.is_visible = false;
Loading diff…

Original Bug Report

reported by vm...@google.com

WebXR DOM Overlay input suppression bypass allows cross-origin input sniffing

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the Chrome Security team. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: A potential logic flaw in WebXR DOM Overlay hit testing allows a malicious subframe to bypass cross-origin input suppression. The implementation fails to suppress events for Out-of-Process Iframes (OOPIFs) and incorrectly compares origins against the outermost main frame instead of the XR session’s origin. This could allow an attacker to sniff pointer coordinates and interactions over embedded third-party content.

Affected files:

  • third_party/blink/renderer/modules/xr/xr_input_source.cc
  • third_party/blink/renderer/modules/xr/xr_session.cc

Estimated timestamp from git blame: 2022-04-22

Description

The WebXR DOM Overlay specification requires that when a user’s pointer intersects with cross-origin content relative to the XR session’s document, controller poses and select events must be suppressed. This prevents a site from sniffing user input (e.g., precise pointer positions or click timing) over embedded third-party content.

There is a potential vulnerability in XRInputSource::ProcessOverlayHitTest() (third_party/blink/renderer/modules/xr/xr_input_source.cc) where the cross-origin detection logic fails in two scenarios, allowing input events to be leaked to the WebXR session owner.

1. OOPIF Bypass (Failing Open on Null Document) When the hit test intersects an iframe, the code attempts to retrieve its document:

HTMLFrameElementBase* frame = DynamicTo<HTMLFrameElementBase>(hit_element);
if (frame) {
  Document* hit_document = frame->contentDocument();
  if (hit_document) {
    // ... suppression logic ...
  }
}
// Visibility defaults to true if we fall through
state_.is_visible = true;

For Out-of-Process Iframes (OOPIFs), the internal frame representation is a RemoteFrame. Because HTMLFrameOwnerElement::contentDocument() attempts to cast the frame to a LocalFrame, it returns nullptr for OOPIFs. The if (hit_document) check fails, the suppression logic is skipped, and the input source remains visible.

2. Incorrect Reference Origin Check If the hit frame is same-process, the code evaluates hit_frame->IsCrossOriginToOutermostMainFrame(). This compares the hit frame’s origin against the top-level outermost main frame, which is incorrect. If the WebXR session is running in a subframe (Origin B) embedded by a top-level document (Origin A), the attacker at Origin B can embed Origin A content in their overlay. The check will compare Origin A to Origin A, return false, and fail to suppress the input, even though Origin A is cross-origin to the XR session owner (Origin B).

Potential Reproduction Steps

Note: These are suggested steps for reproduction as our analysis tooling does not execute code to verify a live proof of concept.

  1. A benign top-level site (https://host.example) embeds an attacker subframe (https://attacker.example) and delegates WebXR permissions: <iframe src="..." allow="xr-spatial-tracking; fullscreen">.
  2. The attacker frame requests an immersive-ar session with the dom-overlay feature enabled.
  3. Inside the attacker’s DOM overlay root, they embed a sensitive cross-origin iframe. This could be an OOPIF (https://sensitive.example) or the top-level host (https://host.example/sensitive).
  4. The user physically taps or interacts with the area over the embedded iframe during the AR session.
  5. Because of the logic flaws described above, the attacker frame successfully receives select events and non-null targetRaySpace poses corresponding to the user’s interaction with the cross-origin content.

Suggested Fix

  1. Handle RemoteFrames: Modify the logic to recognize RemoteFrame intersections. If a RemoteFrame is hit, it is inherently cross-process (and thus cross-origin), so input events should be suppressed immediately without requiring a contentDocument().
  2. Correct Origin Comparison: Instead of checking IsCrossOriginToOutermostMainFrame(), the hit frame’s security origin should be compared against the security origin of the ExecutionContext (the Document) that owns the XRSession.

Evaluated with Chrome root at commit: a1e33f5848218e21d4a16ae2c1bc94e815c30c7f


Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker