CVE-2026-17823
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/blink/renderer/modules/xr/xr_input_source.cc |
modified |
Files Changed
third_party/blink/renderer/modules/xr/xr_input_source.cc
Patch
From d07e615403b61e8112af61becf7db5d1d1d7cdb2 Mon Sep 17 00:00:00 2001
From: Alexander Cooper <alcooper@chromium.org>
Date: Mon, 01 Jun 2026 17:36:58 -0700
Subject: [PATCH] [WebXR] Inspect frame subtree for cross-origin content in DOM Overlay
Ensures that same-origin wrapper iframes cannot bypass input
suppression in WebXR DOM overlays. When processing overlay hit
test results, walk the entire frame subtree rooted at the hit
frame to verify if any descendant frame is remote or cross-origin
with the session.
Fixed: 517628043
Change-Id: I1d09f5d76c7e88570ab3a5164511cdea81cbf60d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7886118
Reviewed-by: Brandon Jones <bajones@chromium.org>
Auto-Submit: Alexander Cooper <alcooper@chromium.org>
Commit-Queue: Brandon Jones <bajones@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1639815}
---
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 e3f08c59..266a0370 100644
--- a/third_party/blink/renderer/modules/xr/xr_input_source.cc
+++ b/third_party/blink/renderer/modules/xr/xr_input_source.cc
@@ -528,15 +528,24 @@
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)) {
+ const SecurityOrigin* session_origin =
+ session_->GetExecutionContext()->GetSecurityOrigin();
+
+ // Ensure that same-origin wrapper iframes cannot be used to bypass input
+ // suppression for nested cross-origin iframes. Walk the entire frame
+ // subtree to check if any descendant frame is cross-origin or remote.
+ for (Frame* node = hit_frame; node != nullptr;
+ node = node->Tree().TraverseNext(hit_frame)) {
+ if (node->IsRemoteFrame()) {
is_cross_origin = true;
+ break;
+ } else {
+ const SecurityOrigin* hit_origin =
+ node->GetSecurityContext()->GetSecurityOrigin();
+ if (!hit_origin->IsSameOriginWith(session_origin)) {
+ is_cross_origin = true;
+ break;
+ }
}
}
Original Bug Report
Potential bypass of WebXR DOM Overlay cross-origin input suppression via nested wrapper iframes
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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A potential security vulnerability exists in WebXR’s DOM Overlay where the cross-origin input suppression mechanism can be bypassed using a same-origin wrapper iframe around cross-origin content. Because the hit-test request lacks the kAllowChildFrameContent flag, the hit-test short-circuits at the wrapper boundary and evaluates it as same-origin, failing to suppress the input. This could potentially allow a malicious site to capture precise user touch coordinates and interaction timings intended for a nested cross-origin iframe.
Affected files:
third_party/blink/renderer/modules/xr/xr_input_source.ccthird_party/blink/renderer/core/layout/layout_embedded_content.cc
Estimated timestamp from git blame: 2020-01-15
Description
There is a potential vulnerability in WebXR’s DOM Overlay where an attacker can bypass cross-origin input suppression to capture user interaction coordinates and select events intended for a cross-origin iframe.
To protect user input from cross-origin pages inside a WebXR DOM Overlay, the browser is designed to set the session’s input source visibility to false and suppress selection events when the user touches cross-origin content. However, the hit-test request used to determine this is performed without the HitTestRequest::kAllowChildFrameContent flag.
Root Cause Analysis
In third_party/blink/renderer/modules/xr/xr_input_source.cc, XRInputSource::ProcessOverlayHitTest executes a hit test to check if the touch coordinate intersects a cross-origin frame:
HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kTouchEvent |
HitTestRequest::kReadOnly |
HitTestRequest::kActive;
HitTestResult result = event_handling_util::HitTestResultInFrame(
overlay_element->GetDocument().GetFrame(), HitTestLocation(point),
hit_type);
Because the HitTestRequest::kAllowChildFrameContent flag is missing from hit_type, hit-testing inside LayoutEmbeddedContent::NodeAtPoint (in third_party/blink/renderer/core/layout/layout_embedded_content.cc) evaluates skip_contents = true at the first-level subframe boundary:
bool skip_contents =
(result.GetHitTestRequest().GetStopNode() == this ||
!result.GetHitTestRequest().AllowsChildFrameContent() || // Evaluates to true
PointOverResizer(result, hit_test_location, accumulated_offset));
If the attacker creates a same-origin wrapper iframe containing a nested cross-origin iframe, the hit-test stops at the outer wrapper boundary and returns the wrapper iframe element as the InnerElement().
Since the wrapper iframe is same-origin with the WebXR session, is_cross_origin evaluates to false in ProcessOverlayHitTest:
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;
}
}
As a result, state_.is_visible is set to true, and the input events are not suppressed, allowing the attacker’s script to retrieve the precise screen touch coordinates through the input source pose.
Potential Steps to Reproduce
Note: These are suggested/potential steps to demonstrate the issue, as our tooling does not have the capability to run code.
- From an attacker origin (e.g.,
https://attacker.example), create a DOM overlay container containing a same-origin wrapper iframe that wraps a cross-origin victim iframe:<div id="ov"> <iframe id="wrap" style="position:absolute;inset:0;border:0" srcdoc='<iframe src="https://victim.example/" style="position:absolute;inset:0;border:0"></iframe>'></iframe> </div> - Request an immersive AR WebXR session with DOM overlay targeting the element
ov:navigator.xr.requestSession('immersive-ar', {requiredFeatures: ['dom-overlay'], domOverlay: {root: ov}}); - Register listeners for input events (
selectstart,select) on theXRSession, and queryframe.getPose(session.inputSources[0].targetRaySpace, refSpace)in the requestAnimationFrame loop. - Perform a screen touch over the area where the cross-origin iframe is rendered.
- Observe that the touch coordinates are returned and select events are successfully received by the attacker’s context, rather than being hidden and suppressed.
Suggested Fix
To resolve this, update the hit-test logic in ProcessOverlayHitTest to:
- Append
HitTestRequest::kAllowChildFrameContentto theHitTestRequest::HitTestRequestTypeso that the hit-test can traverse local child frame hierarchies. - In addition to evaluating
HTMLFrameElementBase(which covers RemoteFrames or frames where hit-testing is skipped), evaluate the origin of the frame hosting the hit element viahit_element->GetDocument().GetFrame()to ensure nested local cross-origin frames are correctly identified.
Evaluated with Chrome root at commit: 5133b93d189b383c37805b1cf3a9d2dbfe8d7379
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
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.