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-componentUI>Browser>Omnibox
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.
Tracker454354281
Fix commite81ab324244b (chromium/src) +85/-0
CWECWE-451
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N
CISA KEVNot listed
CreditedKhalil Zhani
Reported2025-10-23 (per vendor advisory)
Fix landed2025-10-24 (public git)
Disclosed2025-11-05
Patch gap12 days — fix public in source before the advisory

Background

Browser controls
on Android, the top and bottom browser UI (toolbar/omnibox and bottom controls) that slide in and out as the page scrolls; their position is described by a height and a shown ratio (0 = fully hidden, 1 = fully shown).
`RenderWidgetHostViewAndroid::UpdateControls`
the browser-process method that receives controls heights, shown ratios and min-height offsets, compares them with cached prev_* values, and returns whether anything changed so that a browser-controls update gets pushed.
`prev_bottom_shown_pix_`
the cached visible bottom-controls height in pixels (bottom_controls_pix * bottom_controls_shown_ratio), which was previously the only bottom-controls value used for change detection.
`cc::MathUtil::IsFloatNearlyTheSame`
a float comparison with tolerance, used here to decide whether a cached value has changed.

Root Cause Analysis

The enclosing function name is not in the hunk, but the regression test calls rwhva->UpdateControls(...) and the hunk ends with return top_changed || bottom_changed;, so the method is RenderWidgetHostViewAndroid::UpdateControls. In that method, bottom_changed was computed only by comparing bottom_shown_pix (height × ratio) with prev_bottom_shown_pix_. When the shown ratio is 0, bottom_shown_pix is 0 for any height. A change in bottom_controls_pix therefore produced no detected change, the method returned false, and no update was pushed. Per the commit message, the bottom-controls offset should become the new height in that case; without the update, the offset stayed stale and the controls were left partially visible.

The fix adds prev_bottom_controls_pix_ (initialised to 0.f in the constructor, stored at the end of UpdateControls) and ORs a height-changed comparison into bottom_changed. This matches how top controls already worked and tightens change detection.

Key insight
Change detection compared only the product height × ratio. At a 0% shown ratio that product cannot reflect a height change, so a new bottom-controls height was silently dropped. The fix also tracks the raw bottom-controls height.

Attack Path

  1. Bottom controls fully hidden On Android, the page scrolls so the bottom browser controls reach a 0% shown ratio.
  2. Bottom controls height changes While hidden, the bottom-controls height changes (test step 7 changes it from 50.f to 60.f at ratio 0.0f). The diff does not show what triggers the height change in practice.
  3. Update suppressed Before the fix, bottom_shown_pix stayed 0, bottom_changed was false, UpdateControls returned false, and no browser-controls update was pushed.
  4. Controls left partially visible (inferred) With an offset computed for the old height, part of the bottom browser UI (which can contain omnibox elements) stays on screen, misaligned with or overlapping page content. A page could combine this with its own content to confuse the user about browser UI (inferred).

Impact Assessment

This is a browser-process logic flaw in Android browser-controls layout. The result is stale positioning that leaves bottom controls partially visible, not memory corruption. It needs Chrome on Android with bottom browser controls, the controls scrolled fully hidden, and a change in bottom-controls height at that moment. Ordinary page scrolling can reach the hidden state. The diff does not show whether a web page can trigger the height change itself or whether browser UI state must change it, and nothing indicates a compromised renderer, flag or extension is required. Any omnibox spoofing or UI-confusion outcome is (inferred). 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
TEST_F
content/browser/renderer_host/render_widget_host_view_android_unittest.cc
modified
RenderWidgetHostViewAndroidScalingTest
content/browser/renderer_host/render_widget_host_view_android_unittest.cc
modified

Files Changed

  • content/browser/renderer_host/render_widget_host_view_android.cc
  • content/browser/renderer_host/render_widget_host_view_android.h
  • content/browser/renderer_host/render_widget_host_view_android_unittest.cc

Audit Directions

  • Change detection on derived products
    Look for “did it change?” checks that compare a product or ratio (height × ratio, size × scale) and so miss changes in one factor when the other is 0. Compare the raw inputs as well.
  • Top/bottom asymmetry
    Where top controls and bottom controls have parallel logic (offsets, min-heights, caches), diff the two paths for fields one side tracks and the other does not.
  • Partially visible browser chrome
    Audit any path where a suppressed or skipped layout update could leave the omnibox, security indicators or bottom controls half-rendered over web content.
From e81ab324244b7c87c500cc0dc022c40089f86bee Mon Sep 17 00:00:00 2001
From: Patrick Noland <pnoland@google.com>
Date: Fri, 24 Oct 2025 09:00:23 -0700
Subject: [PATCH] Push browser controls update whenever bottom controls height changes

The old logic failed to push updates when height changed at 0% shown
ratio. This was a problem because the offset needs to change when the
height changes at 0% to reflect the new height (i.e. the offset should
become the new height) to avoid leaving the controls partially visible.
The new logic matches existing logic for top controls.

Bug: 454354281
Change-Id: I733d103e8cda58a4dbeefe1fa71282924f7bcd27
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7081525
Commit-Queue: Patrick Noland <pnoland@chromium.org>
Reviewed-by: Nasko Oskov <nasko@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1535059}
---

diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 5b904f6..f91c4c48 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -668,6 +668,7 @@
       prev_top_controls_translate_(0.f),
       prev_top_controls_min_height_offset_pix_(0.f),
       prev_bottom_shown_pix_(0.f),
+      prev_bottom_controls_pix_(0.f),
       prev_bottom_controls_translate_(0.f),
       prev_bottom_controls_min_height_offset_pix_(0.f),
       page_scale_(1.f),
@@ -2253,6 +2254,8 @@
   float bottom_shown_pix = bottom_controls_pix * bottom_controls_shown_ratio;
   bool bottom_changed = !cc::MathUtil::IsFloatNearlyTheSame(
       bottom_shown_pix, prev_bottom_shown_pix_);
+  bottom_changed |= !cc::MathUtil::IsFloatNearlyTheSame(
+      bottom_controls_pix, prev_bottom_controls_pix_);
   float bottom_translate = bottom_controls_pix - bottom_shown_pix;
 
   float bottom_min_height_offset_pix = bottom_controls_min_height_offset;
@@ -2268,6 +2271,7 @@
   prev_bottom_shown_pix_ = bottom_shown_pix;
   prev_bottom_controls_translate_ = bottom_translate;
   prev_bottom_controls_min_height_offset_pix_ = bottom_min_height_offset_pix;
+  prev_bottom_controls_pix_ = bottom_controls_pix;
   controls_initialized_ = true;
   return top_changed || bottom_changed;
 }
diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h
index baa9346..7e1cbb1 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.h
+++ b/content/browser/renderer_host/render_widget_host_view_android.h
@@ -539,6 +539,7 @@
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
                            GestureManagerListensToChildFrames);
   FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAndroidTest, DisplayFeature);
+  FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAndroidTest, UpdateControls);
   FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAndroidFluidResizeBrowserTest,
                            ResizeDefersSynchronizationToNextFrame);
 
@@ -763,6 +764,7 @@
   float prev_top_controls_translate_;
   float prev_top_controls_min_height_offset_pix_;
   float prev_bottom_shown_pix_;
+  float prev_bottom_controls_pix_;
   float prev_bottom_controls_translate_;
   float prev_bottom_controls_min_height_offset_pix_;
   float page_scale_;
diff --git a/content/browser/renderer_host/render_widget_host_view_android_unittest.cc b/content/browser/renderer_host/render_widget_host_view_android_unittest.cc
index 4a243d7..f53031d 100644
--- a/content/browser/renderer_host/render_widget_host_view_android_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android_unittest.cc
@@ -667,6 +667,85 @@
   base::RunLoop().RunUntilIdle();
 }
 
+TEST_F(RenderWidgetHostViewAndroidTest, UpdateControls) {
+  float dip_scale = 1.0f;
+  float top_height = 90.f;
+  float top_ratio = 1.f;
+  float top_min_height = 0.f;
+  float bottom_height = 50.f;
+  float bottom_ratio = 1.f;
+  float bottom_min_height = 0.f;
+
+  // Get the test view instance from the fixture.
+  RenderWidgetHostViewAndroid* rwhva = render_widget_host_view_android();
+
+  // 1. First call should return true as controls are uninitialized.
+  EXPECT_TRUE(rwhva->UpdateControls(dip_scale, top_height, top_ratio,
+                                    top_min_height, bottom_height, bottom_ratio,
+                                    bottom_min_height));
+
+  EXPECT_FALSE(rwhva->UpdateControls(dip_scale, top_height, top_ratio,
+                                     top_min_height, bottom_height,
+                                     bottom_ratio, bottom_min_height));
+
+  // 3. Change top_controls_height.
+  float new_top_height = 100.f;
+  EXPECT_TRUE(rwhva->UpdateControls(dip_scale, new_top_height, top_ratio,
+                                    top_min_height, bottom_height, bottom_ratio,
+                                    bottom_min_height));
+  // Call again with same values, should return false.
+  EXPECT_FALSE(rwhva->UpdateControls(dip_scale, new_top_height, top_ratio,
+                                     top_min_height, bottom_height,
+                                     bottom_ratio, bottom_min_height));
+
+  // 4. Change top_controls_shown_ratio.
+  float new_top_ratio = 0.5f;
+  EXPECT_TRUE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                    top_min_height, bottom_height, bottom_ratio,
+                                    bottom_min_height));
+  EXPECT_FALSE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                     top_min_height, bottom_height,
+                                     bottom_ratio, bottom_min_height));
+
+  // 5. Change top_controls_min_height_offset.
+  float new_top_min_height = 10.f;
+  EXPECT_TRUE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                    new_top_min_height, bottom_height,
+                                    bottom_ratio, bottom_min_height));
+  // Call again with same values, should return false.
+  EXPECT_FALSE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                     new_top_min_height, bottom_height,
+                                     bottom_ratio, bottom_min_height));
+
+  // 6. Change bottom_controls_shown_ratio.
+  float new_bottom_ratio = 0.0f;
+  EXPECT_TRUE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                    new_top_min_height, bottom_height,
+                                    new_bottom_ratio, bottom_min_height));
+
+  EXPECT_FALSE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                     new_top_min_height, bottom_height,
+                                     new_bottom_ratio, bottom_min_height));
+
+  // 7. Change bottom_controls_height while at 0% shown ratio.
+  float new_bottom_height = 60.f;
+  EXPECT_TRUE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                    new_top_min_height, new_bottom_height,
+                                    new_bottom_ratio, bottom_min_height));
+  EXPECT_FALSE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                     new_top_min_height, new_bottom_height,
+                                     new_bottom_ratio, bottom_min_height));
+
+  // 8. Change bottom_controls_min_height_offset.
+  float new_bottom_min_height = 10.f;
+  EXPECT_TRUE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                    new_top_min_height, new_bottom_height,
+                                    new_bottom_ratio, new_bottom_min_height));
+  EXPECT_FALSE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                     new_top_min_height, new_bottom_height,
+                                     new_bottom_ratio, new_bottom_min_height));
+}
+
 // Test for scaling.
 class RenderWidgetHostViewAndroidScalingTest
     : public RenderWidgetHostViewAndroidTest {
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/renderer_host/render_widget_host_view_android_unittest.cc b/content/browser/renderer_host/render_widget_host_view_android_unittest.cc
index 4a243d7..f53031d 100644
--- a/content/browser/renderer_host/render_widget_host_view_android_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android_unittest.cc
@@ -667,6 +667,85 @@
   base::RunLoop().RunUntilIdle();
 }
 
+TEST_F(RenderWidgetHostViewAndroidTest, UpdateControls) {
+  float dip_scale = 1.0f;
+  float top_height = 90.f;
+  float top_ratio = 1.f;
+  float top_min_height = 0.f;
+  float bottom_height = 50.f;
+  float bottom_ratio = 1.f;
+  float bottom_min_height = 0.f;
+
+  // Get the test view instance from the fixture.
+  RenderWidgetHostViewAndroid* rwhva = render_widget_host_view_android();
+
+  // 1. First call should return true as controls are uninitialized.
+  EXPECT_TRUE(rwhva->UpdateControls(dip_scale, top_height, top_ratio,
+                                    top_min_height, bottom_height, bottom_ratio,
+                                    bottom_min_height));
+
+  EXPECT_FALSE(rwhva->UpdateControls(dip_scale, top_height, top_ratio,
+                                     top_min_height, bottom_height,
+                                     bottom_ratio, bottom_min_height));
+
+  // 3. Change top_controls_height.
+  float new_top_height = 100.f;
+  EXPECT_TRUE(rwhva->UpdateControls(dip_scale, new_top_height, top_ratio,
+                                    top_min_height, bottom_height, bottom_ratio,
+                                    bottom_min_height));
+  // Call again with same values, should return false.
+  EXPECT_FALSE(rwhva->UpdateControls(dip_scale, new_top_height, top_ratio,
+                                     top_min_height, bottom_height,
+                                     bottom_ratio, bottom_min_height));
+
+  // 4. Change top_controls_shown_ratio.
+  float new_top_ratio = 0.5f;
+  EXPECT_TRUE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                    top_min_height, bottom_height, bottom_ratio,
+                                    bottom_min_height));
+  EXPECT_FALSE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                     top_min_height, bottom_height,
+                                     bottom_ratio, bottom_min_height));
+
+  // 5. Change top_controls_min_height_offset.
+  float new_top_min_height = 10.f;
+  EXPECT_TRUE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                    new_top_min_height, bottom_height,
+                                    bottom_ratio, bottom_min_height));
+  // Call again with same values, should return false.
+  EXPECT_FALSE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                     new_top_min_height, bottom_height,
+                                     bottom_ratio, bottom_min_height));
+
+  // 6. Change bottom_controls_shown_ratio.
+  float new_bottom_ratio = 0.0f;
+  EXPECT_TRUE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                    new_top_min_height, bottom_height,
+                                    new_bottom_ratio, bottom_min_height));
+
+  EXPECT_FALSE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                     new_top_min_height, bottom_height,
+                                     new_bottom_ratio, bottom_min_height));
+
+  // 7. Change bottom_controls_height while at 0% shown ratio.
+  float new_bottom_height = 60.f;
+  EXPECT_TRUE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                    new_top_min_height, new_bottom_height,
+                                    new_bottom_ratio, bottom_min_height));
+  EXPECT_FALSE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                     new_top_min_height, new_bottom_height,
+                                     new_bottom_ratio, bottom_min_height));
+
+  // 8. Change bottom_controls_min_height_offset.
+  float new_bottom_min_height = 10.f;
+  EXPECT_TRUE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                    new_top_min_height, new_bottom_height,
+                                    new_bottom_ratio, new_bottom_min_height));
+  EXPECT_FALSE(rwhva->UpdateControls(dip_scale, new_top_height, new_top_ratio,
+                                     new_top_min_height, new_bottom_height,
+                                     new_bottom_ratio, new_bottom_min_height));
+}
+
 // Test for scaling.
 class RenderWidgetHostViewAndroidScalingTest
     : public RenderWidgetHostViewAndroidTest {
Loading diff…

Original Bug Report

reported by ch...@gmail.com

Chrome on Android: spoofing issue caused by bottom address bar

Steps to reproduce the problem

(Similar to issue issue 437147699).

  1. Open index.html or https://lbstyle.github.io/bin.html in an incognito window (to reproduce constantly).
  2. Tap the “Click” button.
  3. Tap inside the first input field and wait.
  4. When the alert pops up, tap OK.

Problem Description

The address bar (omnibox) shows google.com, but the page content is incorrect.

Summary

Chrome on Android: spoofing issue caused by bottom address bar

Additional Data

Category: Security
Chrome Channel: Not sure
Regression: N/A \

View on issue tracker