CVE-2025-12447
Overview
Background
- `CompositorViewHolder`
- the Android Java view that hosts Chrome’s compositor and watches the current
Tabthrough an anonymousTabObserver(mTabObserver). It tracks touch gestures to decide whether content is “in motion” (updateInMotion()). - `mNumGestureActiveTouches`
- a counter raised on gesture-begin acks and lowered by
onGestureEnd(). The commit message says a non-zero value “suppresses captures until the gesture ends”. - Captures
- on Android, browser UI such as the toolbar/omnibox is drawn by the compositor from captured bitmaps. While captures are suppressed, the composited UI can lag behind the real state (inferred from the commit message; the capture code itself is not in the diff).
- Unbalanced acks
- a gesture-begin with no matching end, or the reverse. The commit names two causes: switching tabs between acks, and a renderer crash.
Root Cause Analysis
CompositorViewHolder trusted that every gesture begin would be matched by exactly one onGestureEnd() on the same tab. If the tab changed or the renderer crashed mid-gesture, the end ack never arrived. mNumGestureActiveTouches then stayed above zero, so the holder kept treating content as in motion and kept suppressing captures. In the other direction, an extra onGestureEnd() could push the count below zero, since the old code did a plain mNumGestureActiveTouches--. The advisory classifies this as incorrect Omnibox security UI. A plausible link is that suppressed captures leave the composited toolbar/omnibox showing stale content, such as a previous URL or security state (inferred; the diff does not include the capture or omnibox code).
The fix restores the invariant in three ways. It resets the counter to 0 on onCrash(Tab). It resets it to 0 when a new non-null tab is attached and observed. It clamps onGestureEnd() at 0 with Math.max(mNumGestureActiveTouches - 1, 0).
Attack Path
- Start a touch gesture
The user touches the page, and a gesture-begin ack raises
mNumGestureActiveTouchesinCompositorViewHolder. - Break the begin/end pairing Before the gesture-end ack arrives, the renderer crashes or the active tab changes. Page-driven ways to force either are (inferred), and the commit names these two causes.
- Counter stays non-zero
With no matching
onGestureEnd(), the counter stays positive,updateInMotion()keeps reporting motion, and captures stay suppressed indefinitely. - Stale browser UI is shown The compositor keeps showing an outdated toolbar/omnibox capture, so the user may see a URL or security indicator that does not match the content on screen (inferred).
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java |
modified |
Files Changed
chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
Audit Directions
- Counters fed by cross-process acksLook for begin/end, show/hide or acquire/release counters updated from renderer acks (gesture, scroll, fling, IME, fullscreen) that are not reset when the observed tab,
WebContentsor renderer process changes. - Suppression flags guarding security UIAudit every condition that defers updating the toolbar, URL bar or security indicators, such as “in motion”, “animating” or “capture pending”. Confirm each one has a guaranteed exit on crash, navigation and tab swap.
- Observer swap without state resetWhen code calls
removeObserveron the old tab andaddObserveron a new one, check that per-tab state stored on the holder is reset as well, not kept across the switch.
Patch
From 649828b3c37c0b4b23f61857c6c0398e7ee590e8 Mon Sep 17 00:00:00 2001 From: Patrick Noland <[email protected]> Date: Thu, 04 Sep 2025 13:57:15 -0700 Subject: [PATCH] Reset active gesture count on tab change or crash The active gesture count suppresses captures until the gesture ends. This value can become invalid if we don't receive a balanced number of acks for gesture begins and ends. Two ways that can happen are changing tabs between acks or crashes; regardless, it's safe to reset the value in both these cases. Bug: 442636157, 434751393 Change-Id: Idb6be4ed8b53bdc2e2f594db01813c2e13c52801 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6917456 Reviewed-by: Sky Malice <[email protected]> Commit-Queue: Patrick Noland <[email protected]> Cr-Commit-Position: refs/heads/main@{#1511136} --- diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java index 551d47c..aef02b8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java @@ -318,6 +318,11 @@ } @Override + public void onCrash(Tab tab) { + mNumGestureActiveTouches = 0; + } + + @Override public void onDidFinishNavigationInPrimaryMainFrame( Tab tab, NavigationHandle navigation) { if (!navigation.isSameDocument() && navigation.hasCommitted()) { @@ -337,7 +342,7 @@ @Override public void onGestureEnd() { - mNumGestureActiveTouches--; + mNumGestureActiveTouches = Math.max(mNumGestureActiveTouches - 1, 0); updateInMotion(); } }; @@ -1541,6 +1546,7 @@ mHasKeyboardGeometryChangeFired = false; if (mTabVisible != null) mTabVisible.removeObserver(mTabObserver); if (tab != null) { + mNumGestureActiveTouches = 0; tab.addObserver(mTabObserver); mCompositorView.onTabChanged(); }
Original Bug Report
Chrome on Android: URL spoof triggered by address bar position Change
Steps to reproduce the problem
- Open the test case page and tap the “Try it” button.
- Keep your finger pressed inside the page until an alert dialog appears.
- Try to move the address bar to the bottom, then dismiss the alert dialog.
Problem Description
When moving the Chrome address bar to the bottom of the screen, the URL displayed in the address bar changes to google.com/csi, but the actual page content does not correspond to this URL. This mismatch between the visible URL and page content can mislead users into believing they are on a trusted site when they are not, potentially enabling URL spoofing attacks. This behavior poses a security risk by undermining user trust in the address bar’s accuracy.
This issue appears to be similar to Issue 437147699, which also involves URL spoofing vulnerabilities related to address bar inconsistencies.
Summary
Chrome on Android: URL spoof triggered by address bar position Change
Additional Data
Category: Security
Chrome Channel: Canary
Regression: N/A \