Medium watch CVSS 4.2 chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
4.2
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Omnibox
DescriptionInappropriate implementation in Omnibox
ComponentOmnibox
Sub-componentMobar
Bug ClassLogic Error
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker452392032
Fix commit316d6696542c (chromium/src) +57/-24
CWECWE-451
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:L
CISA KEVNot listed
CreditedHafiizh
Reported2025-10-16 (per vendor advisory)
Fix landed2025-10-22 (public git)
Disclosed2025-11-05
Patch gap14 days — fix public in source before the advisory

Background

Mini origin bar ("mobar")
an Android toolbar mode, driven by MiniOriginBarController, that shrinks the location bar to a compact origin display (tracked through setMinimizationProgress) while the user is typing into a form.
Keyboard accessory
the autofill/manual-filling bar shown above the soft keyboard; its height is published through mKeyboardAccessoryHeightSupplier and computed in ManualFillingMediator.
`ToolbarControlContainer` / `mControlContainer`
the Android view that hosts the toolbar and omnibox; when the toolbar is at the bottom, ToolbarPositionController positions it with Y-translation and layout params.
Multi-window
Android split-screen/freeform mode, in which (per the commit) the control container was not drawn when its pre-translation position overlapped the keyboard accessory, and no insets animation runs.

Root Cause Analysis

With the toolbar at the bottom, ToolbarPositionController.updateViewOffset placed the control container above the keyboard accessory by subtracting mKeyboardAccessoryHeightSupplier.get() from layerYOffset, so the container’s untranslated layout position overlapped the accessory. The commit reports that in multi-window Android then did not draw ToolbarControlContainer at all. The omnibox/origin display therefore disappeared exactly while the user was interacting with a page form, which breaks the rule that browser UI showing the page’s identity stays visible.

The fix stops folding the accessory height into the translation. A new mKeyboardAccessoryHeightObserver (registered in place of mKeyboardHeightToolbarCallback) instead sets mControlContainer’s bottomMargin to the accessory height when controls are at ControlsPosition.BOTTOM, and to 0 when they are at ControlsPosition.TOP.

The patch also makes ManualFillingMediator’s reported heights aware of AUTOFILL_ENABLE_KEYBOARD_ACCESSORY_CHIP_REDESIGN, since the redesigned accessory had been reporting the old dimension. It also reorders MiniOriginBarController so setMinimizationProgress(1.0f) runs after showMiniOriginBar() when the visibility change happens without an animation.

Key insight
The omnibox container was moved above the keyboard accessory purely by translation, which left its real layout bounds overlapping the accessory. Multi-window then skipped drawing it. The fix reserves the accessory’s space with a real bottom margin and uses the correct, feature-aware accessory height.

Attack Path

  1. Preconditions on device The victim uses Chrome on Android with the toolbar at the bottom, in multi-window mode, with the mini-origin-bar behaviour active.
  2. Page focuses a form field Attacker-controlled page content shows an input. When it is focused, the soft keyboard and keyboard accessory appear, and the mini origin bar is supposed to show the page origin.
  3. Omnibox not drawn Before the fix, updateViewOffset subtracted the accessory height from the translation. The container’s pre-translation bounds overlapped the accessory, and in multi-window the control container (and its origin display) was not rendered.
  4. Origin hidden during data entry (inferred) With no visible URL or origin, the page could present itself as a different site while the user types credentials or other data, giving a UI-spoofing opportunity (inferred).

Impact Assessment

This is a browser-process UI rendering flaw on Android. It can hide the omnibox/origin display; it does not corrupt memory. Reaching it requires an Android device in multi-window mode, a bottom toolbar position, a focused form field with the keyboard accessory visible, and normal user interaction. The diff does not show whether the mini-origin-bar or bottom-toolbar features are on by default. No compromised renderer or extension is needed, because ordinary web content can provide the form. Any spoofing or phishing outcome is (inferred) from the omnibox becoming invisible. The vendor rates this medium (“Inappropriate implementation in Omnibox”). The provided metadata does not say whether it was exploited in the wild.

Changed Functions

FunctionChangeNotes
if
chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java
modified
if
chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java
modified

Files Changed

  • chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
  • chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
  • chrome/browser/keyboard_accessory/android/internal/java/res/values/dimens.xml
  • chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java
  • chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java
  • chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionControllerTest.java

Audit Directions

  • Security UI positioned only by translation
    Look for omnibox, origin, permission-chip or security-indicator views that are moved with setTranslationY while their layout bounds overlap other windows or bars. Rendering optimisations, especially in multi-window or freeform modes, may skip drawing them.
  • Stale or feature-unaware dimension sources
    Find places where a redesigned component (behind a ChromeFeatureList flag) still reports a legacy R.dimen size to layout code that positions browser chrome.
  • Animation-only state transitions
    Audit controllers that only finish a state (progress, visibility, offsets) in animation callbacks, and confirm the end state is still applied in modes where animations or insets callbacks never fire.
From 316d6696542c7cd0307eeb8df3a914a8eda9989f Mon Sep 17 00:00:00 2001
From: Patrick Noland <pnoland@google.com>
Date: Wed, 22 Oct 2025 12:30:26 -0700
Subject: [PATCH] [mobar] Fix keyboard accessory overlap in multiwindow

For reasons as yet unknown to me, multi-window seems to cause
ToolbarControlContainer to not be drawn when its pre-translation
position overlaps with the keyboard accessory. This may be an
optimization of sorts; I was not able to turn it off by fiddling with
clipChildren/clipToBounds.

Instead, we resolve it by removing kb accessory height from the
translation calculation, using it instead to set the bottom margin. This
should be safe since we don't use the bottom margin for another purpose
and performant since the keyboard accessory doesn't change height often.

Two related issues are resolved at the same time:
* The revamped kb accessory reports the wrong height, referencing the
  old dimen. This is changed to be feature aware.
* There is no insets animation in multiwindow, meaning minimization
  progress isn't set properly. We now explicitly call it when skipping
  the animation.

Bug: 452392032, 438136964
Change-Id: I0df6bffdbbb0885e70b5c7ff0614faa5447b93e1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7069866
Reviewed-by: Tomasz Wiszkowski <ender@google.com>
Commit-Queue: Patrick Noland <pnoland@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1533841}
---

diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
index 33eeae5..95eacd57 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
@@ -1064,12 +1064,22 @@
     }
 
     private @Px int getBarHeightWithoutShadow() {
-        return mActivity
-                .getResources()
-                .getDimensionPixelSize(R.dimen.keyboard_accessory_suggestion_height);
+        if (ChromeFeatureList.isEnabled(
+                ChromeFeatureList.AUTOFILL_ENABLE_KEYBOARD_ACCESSORY_CHIP_REDESIGN)) {
+            return mActivity
+                    .getResources()
+                    .getDimensionPixelSize(R.dimen.keyboard_accessory_height_redesign);
+        }
+        return mActivity.getResources().getDimensionPixelSize(R.dimen.keyboard_accessory_height);
     }
 
     private @Px int getHeaderHeight() {
+        if (ChromeFeatureList.isEnabled(
+                ChromeFeatureList.AUTOFILL_ENABLE_KEYBOARD_ACCESSORY_CHIP_REDESIGN)) {
+            return mActivity
+                    .getResources()
+                    .getDimensionPixelSize(R.dimen.keyboard_accessory_height_with_shadow_redesign);
+        }
         return mActivity
                 .getResources()
                 .getDimensionPixelSize(R.dimen.keyboard_accessory_height_with_shadow);
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
index c53e72f1..334a699 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
@@ -123,7 +123,8 @@
 @Config(manifest = Config.NONE)
 @Features.EnableFeatures({
     ChromeFeatureList.AUTOFILL_ANDROID_DESKTOP_SUPPRESS_ACCESSORY_ON_EMPTY,
-    ChromeFeatureList.AUTOFILL_ANDROID_DESKTOP_KEYBOARD_ACCESSORY_REVAMP
+    ChromeFeatureList.AUTOFILL_ANDROID_DESKTOP_KEYBOARD_ACCESSORY_REVAMP,
+    ChromeFeatureList.AUTOFILL_ENABLE_KEYBOARD_ACCESSORY_CHIP_REDESIGN
 })
 public class ManualFillingControllerTest {
     private static final int sKeyboardHeightDp = 100;
@@ -1701,6 +1702,8 @@
         // Return the correct keyboard_accessory_height for the current density:
         when(mMockResources.getDimensionPixelSize(R.dimen.keyboard_accessory_suggestion_height))
                 .thenReturn((int) (density * 48));
+        when(mMockResources.getDimensionPixelSize(R.dimen.keyboard_accessory_height_redesign))
+                .thenReturn((int) (density * 48));
     }
 
     /**
diff --git a/chrome/browser/keyboard_accessory/android/internal/java/res/values/dimens.xml b/chrome/browser/keyboard_accessory/android/internal/java/res/values/dimens.xml
index 270a49f..32e0eb4 100644
--- a/chrome/browser/keyboard_accessory/android/internal/java/res/values/dimens.xml
+++ b/chrome/browser/keyboard_accessory/android/internal/java/res/values/dimens.xml
@@ -11,6 +11,7 @@
     <dimen name="keyboard_accessory_horizontal_padding">6dp</dimen>
     <dimen name="keyboard_accessory_height">48dp</dimen>
     <dimen name="keyboard_accessory_height_redesign">64dp</dimen>
+    <dimen name="keyboard_accessory_height_with_shadow_redesign">72dp</dimen>
     <dimen name="keyboard_accessory_chip_min_height_redesign">64dp</dimen>
     <dimen name="keyboard_accessory_chip_vertical_insets">8dp</dimen>
     <dimen name="keyboard_accessory_chip_vertical_insets_large_font">2dp</dimen>
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java
index f8e7527..9394ad06 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java
@@ -243,17 +243,19 @@
         boolean finishedShowing = newMiniOriginState == MiniOriginState.SHOWING;
         mMiniOriginBarState = newMiniOriginState;
 
-        if (finishedShowing) {
-            setMinimizationProgress(1.0f);
+        if (!isChangingVisibility) {
+            if (finishedShowing) setMinimizationProgress(1.0f);
+            return;
         }
 
-        if (!isChangingVisibility) return;
-
         if (isMiniOriginBarVisibleForState(newMiniOriginState)) {
             // Cache the location bar's layout params now, since we are about to mutate them.
             mDefaultLocationBarLayoutParams =
                     (FrameLayout.LayoutParams) mLocationBar.getContainerView().getLayoutParams();
             showMiniOriginBar();
+            if (finishedShowing) {
+                setMinimizationProgress(1.0f);
+            }
         } else {
             hideMiniOriginBar();
         }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java
index 553dd369..1f0f955 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java
@@ -54,6 +54,9 @@
 /** Class responsible for managing the position (top, bottom) of the browsing mode toolbar. */
 @NullMarked
 public class ToolbarPositionController implements OnSharedPreferenceChangeListener {
+
+    private final Callback<Integer> mKeyboardAccessoryHeightObserver;
+
     @IntDef({
         ToolbarPositionAndSource.TOP_LONG_PRESS,
         ToolbarPositionAndSource.TOP_SETTINGS,
@@ -332,11 +335,19 @@
                 (focused) -> updateViewOffset(mProgressBarLayer, mToolbarProgressBarContainer);
         mControlContainerTranslationCallback =
                 (offset) -> updateViewOffset(mBottomToolbarLayer, mControlContainer.getView());
+        mKeyboardAccessoryHeightObserver =
+                (height) -> {
+                    if (mCurrentPosition.get() == ControlsPosition.TOP) {
+                        mControlContainer.mutateLayoutParams().bottomMargin = 0;
+                        return;
+                    }
+                    mControlContainer.mutateLayoutParams().bottomMargin = height;
+                };
         mControlContainerHeightCallback = this::updateControlContainerHeight;
         mControlContainerHeightSupplier.addSyncObserverAndCallIfNonNull(
                 mControlContainerHeightCallback);
 
-        mKeyboardAccessoryHeightSupplier.addObserver(mKeyboardHeightToolbarCallback);
+        mKeyboardAccessoryHeightSupplier.addObserver(mKeyboardAccessoryHeightObserver);
         mKeyboardAccessoryHeightSupplier.addObserver(mKeyboardHeightProgressBarCallback);
         mKeyboardVisibilityDelegate.addKeyboardVisibilityListener(
                 mKeyboardVisibilityViewOffsetCallback);
@@ -374,6 +385,7 @@
         mIsFormFieldFocusedSupplier.removeObserver(mFormFieldViewOffsetCallback);
         mControlContainerTranslationSupplier.removeObserver(mControlContainerTranslationCallback);
         mControlContainerHeightSupplier.removeObserver(mControlContainerHeightCallback);
+        mKeyboardAccessoryHeightSupplier.removeObserver(mKeyboardAccessoryHeightObserver);
         if (mTopInsetCoordinatorObserver != null) {
             var topInsetCoordinator = mTopInsetCoordinatorSupplier.get();
             if (topInsetCoordinator != null) {
@@ -636,18 +648,15 @@
     private void updateViewOffset(BottomControlsLayerWithOffset layer, View viewForLayer) {
         if (mLayerVisibility != LayerVisibility.VISIBLE) return;
 
-        int keyboardAccessoryHeight = mKeyboardAccessoryHeightSupplier.get();
-        int layerYOffset =
-                layer.getLayerOffsetPx()
-                        - keyboardAccessoryHeight
-                        + mControlContainerTranslationSupplier.get();
+        int layerYOffset = layer.getLayerOffsetPx() + mControlContainerTranslationSupplier.get();
         int chinHeight =
                 mBottomControlsStacker.isLayerVisible(LayerType.BOTTOM_CHIN)
                         ? mBottomControlsStacker.getHeightFromLayerToBottom(LayerType.BOTTOM_CHIN)
                         : 0;
         // The chin overlaps with the accessory when they're both visible. To avoid double counting,
         // remove the chin's height from the final offset.
-        boolean chinVisibleWithAccessory = keyboardAccessoryHeight > 0 && chinHeight > 0;
+        boolean chinVisibleWithAccessory =
+                mKeyboardAccessoryHeightSupplier.get() > 0 && chinHeight > 0;
         if (chinVisibleWithAccessory) {
             layerYOffset += chinHeight;
         }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionControllerTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionControllerTest.java
index 967632d..00876e1e 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionControllerTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionControllerTest.java
@@ -998,7 +998,8 @@
         verify(mControlContainerView).setTranslationY(baseTranslation);
 
         final int chinHeight = 36;
-        int keybpardAccessoryHeight = 100;
+        int keyboardAccessoryHeight = 100;
+        mKeyboardAccessoryHeightSupplier.set(keyboardAccessoryHeight);
Loading diff…

Original Bug Report

reported by sa...@gmail.com

When in split-view mode, the mini address bar does not appear above the virtual keyboard, leading to a spoof.

VULNERABILITY DETAILS This vulnerability is similar to https://issues.chromium.org/issues/439262604 (iOS version), but this bug occurs on the Android version. When in split-view mode, the mini address bar does not appear above the virtual keyboard.

VERSION Chrome Version: [143.0.7472.0] + [Canary] Operating System: Android 16 Device: Samsung S25 Edge

REPRODUCTION CASE

  1. Open https://you-can-billowy-nimble-login-secure-docs-google-source-attacker.com/spoofnewxiov.html or open spoofnewxiov.html on a local web server using the https:// protocol and open it in split-view mode.
  2. Click on the “google” link.
View on issue tracker