CVE-2026-17794
Overview
Files Changed
cc/mojom/render_frame_metadata_mojom_traits.ccchrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
Patch
From 0a5df90c851f96937359903f9cfe16c734fe64de Mon Sep 17 00:00:00 2001
From: Mark Schillaci <mschillaci@google.com>
Date: Thu, 04 Jun 2026 16:33:42 -0700
Subject: [PATCH] Clamp top and bottom control offsets to be on screen
This CL updates the deserialization traits for RenderFrameMetadata to
verify that the top and bottom control heights are finite and
non-negative. Unexpected values could cause the browser to render the
controls incorrectly or offscreen. By clamping the values, we can ensure
that the top controls are always visible.
Bug: 514067070
Change-Id: Ic173324baf0f364c10cac0d484cc19a587056ad7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7868572
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Wenyu Fu <wenyufu@chromium.org>
Commit-Queue: Mark Schillaci <mschillaci@google.com>
Reviewed-by: Sinan Sahin <sinansahin@google.com>
Cr-Commit-Position: refs/heads/main@{#1642022}
---
diff --git a/cc/mojom/render_frame_metadata_mojom_traits.cc b/cc/mojom/render_frame_metadata_mojom_traits.cc
index 8d22520..10b28ad 100644
--- a/cc/mojom/render_frame_metadata_mojom_traits.cc
+++ b/cc/mojom/render_frame_metadata_mojom_traits.cc
@@ -35,11 +35,29 @@
out->external_page_scale_factor = data.external_page_scale_factor();
out->top_controls_height = data.top_controls_height();
out->top_controls_shown_ratio = data.top_controls_shown_ratio();
+
+ // Ensure top controls are finite and non-negative heights so that the top
+ // controls cannot be moved offscreen by the renderer.
+ if (!std::isfinite(out->top_controls_height) ||
+ out->top_controls_height < 0 ||
+ !std::isfinite(out->top_controls_shown_ratio)) {
+ return false;
+ }
+
out->primary_main_frame_item_sequence_number =
data.primary_main_frame_item_sequence_number();
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
out->bottom_controls_height = data.bottom_controls_height();
out->bottom_controls_shown_ratio = data.bottom_controls_shown_ratio();
+
+ // Ensure bottom controls are finite and non-negative heights so that the
+ // bottom controls cannot be moved offscreen by the renderer.
+ if (!std::isfinite(out->bottom_controls_height) ||
+ out->bottom_controls_height < 0 ||
+ !std::isfinite(out->bottom_controls_shown_ratio)) {
+ return false;
+ }
+
out->top_controls_min_height_offset = data.top_controls_min_height_offset();
out->bottom_controls_min_height_offset =
data.bottom_controls_min_height_offset();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
index fe3af42..ad042469 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
@@ -625,8 +625,8 @@
// Make the bottom controls height smaller by the shadow height so that
// the shadow is displayed over the bottom of the WebContents.
- mBrowserControlsSizer.setBottomControlsHeight(
- minHeight + height - mShadowHeightPx, minHeight);
+ int bottomControlsHeight = Math.max(0, height - mShadowHeightPx);
+ mBrowserControlsSizer.setBottomControlsHeight(minHeight + bottomControlsHeight, minHeight);
}
// SwipeGestureListener.SwipeHandler methods
Original Bug Report
Potential URL bar spoofing on Android via unvalidated top-controls metadata
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 URL bar spoofing vulnerability exists in Chrome for Android where a compromised renderer can move the authoritative browser UI off-screen. This is due to a lack of range validation for top-controls metadata in the browser process. An attacker can use this to hide the real URL bar and paint a fake origin in the web content.
Affected files:
chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.javacontent/browser/renderer_host/render_widget_host_view_android.cccc/mojom/render_frame_metadata_mojom_traits.ccchrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/TopControlsStacker.javachrome/browser/android/compositor/layer/toolbar_layer.ccchrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/StaticLayout.javachrome/browser/android/compositor/scene_layer/static_tab_scene_layer.cc
Estimated timestamp from git blame: 2018-05-15
Summary
A compromised renderer process can potentially perform a complete URL bar and origin spoof on Chrome for Android. By providing crafted cc::mojom::RenderFrameMetadata values, an attacker can displace the security-critical browser-rendered URL bar off-screen while positioning malicious web content in its place. This is possible because the browser process does not adequately validate the range or sanity of renderer-supplied control offsets.
Vulnerability Details
The vulnerability exists in the handling of top-controls metadata sent from the renderer to the browser:
-
Missing Mojo Validation: In
cc/mojom/render_frame_metadata_mojom_traits.cc, thetop_controls_heightandtop_controls_shown_ratiofields are deserialized as raw floats without range checks. A compromised renderer can provide extreme or negative values. -
Malformed Offset Calculation (C++): In
content/browser/renderer_host/render_widget_host_view_android.cc, the browser calculates a translation offset (top_translate) using the formula:top_translate = (top_controls_height * top_controls_shown_ratio) - top_controls_height. By supplying a large negative height (e.g., -10000.0) and a zero ratio, a renderer triggers a large positive offset (+10000.0). -
Incomplete Clamping (Java): This malformed offset is passed via JNI to
BrowserControlsManager.java. In thesetPositionsForTabmethod, the code applies a lower-bound clamp (Math.max(topControlsOffset, -getTopControlsHeight())) to prevent the controls from moving too far up, but it fails to apply an upper-bound clamp. Consequently, the large positive offset (10000) persists. -
UI Displacement and Hiding: This offset causes the browser to translate the Android View-based toolbar 10000 pixels down (off-screen). Additionally, since the browser calculates a “hidden ratio” greater than zero, it may set the real toolbar view to
INVISIBLE. Meanwhile, the web content is positioned at Y=0. The renderer can then fill the space where the omnibox should be with its own content, mimicking a legitimate browser UI.
Impact
This is a high-severity security boundary bypass. The browser-process-rendered URL bar is the authoritative source of truth for a site’s identity. If it can be hidden or replaced by web content, a compromised renderer can effectively spoof any origin (e.g., a login page for a financial institution) to the user.
Potential Reproduction Steps
- Gain control of a renderer process (precondition).
- Manually send a
cc::mojom::RenderFrameMetadataMojo message to the browser withtop_controls_height = -10000.0fandtop_controls_shown_ratio = 0.0f. - Observe that the legitimate URL bar is shifted off-screen or disappears.
- The renderer can now paint a fake UI in the top area of the screen where the omnibox normally resides.
Suggested Fix
- Implement strict range validation in
cc/mojom/render_frame_metadata_mojom_traits.cc. Ensuretop_controls_heightis non-negative andtop_controls_shown_ratiois finite and within the [0, 1] range. - Add an upper-bound clamp in
BrowserControlsManager.java(e.g.,Math.min(topControlsOffset, 0)) to ensure the controls cannot be moved downward beyond their resting position.
Evaluated with Chrome root at commit: 1a8d40fc44df2088d5945c0bf53584038aa1614a
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.