High watch ✔︎ verified CVSS 5 chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
5
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Input
DescriptionInappropriate implementation in Input
ComponentInput
Sub-componentAndroid
Bug ClassLogic Error
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety — factors set by independent review by AI
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.
Independent review by AIConfirmed
A second, independent AI reviewer (not a human) re-derived this analysis from the fixing commit and the vendor advisory, looking for errors.
Tracker513995565
Fix commit253b422fbd1b (chromium/src) +15/-2
CWECWE-346 (Origin validation error)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:L
CISA KEVNot listed
CreditedGoogle
Reported2026-05-17 (per vendor advisory)
Fix landed2026-05-18 (public git)
Disclosed2026-05-27
Patch gap9 days — fix public in source before the advisory

Background

`ImeAdapterImpl`
the Java object, tied to a WebContents, that bridges Android IME/input state to Blink over Mojo (content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java).
`ImeRenderWidgetHostImpl`
an inner class implementing the org.chromium.blink.mojom.ImeRenderWidgetHost Mojo interface that forwards renderer messages such as updateCursorAnchorInfo to the adapter (ImeAdapterImpl.java:242).
`bindImeRenderHost`
the @CalledByNative entry point that wraps a native Mojo pipe handle and binds a fresh ImeRenderWidgetHostImpl to it (ImeAdapterImpl.java:1818).

Root Cause Analysis

Each time the native side hands Java a new pipe, bindImeRenderHost constructs new ImeRenderWidgetHostImpl(this, handle) and discards the reference (ImeAdapterImpl.java:1822), relying on the comment’s claim that “Mojo will” keep it alive (ImeAdapterImpl.java:1812-1813). The constructor binds the implementation to the handle via MANAGER.bind(this, mHandle) (ImeAdapterImpl.java:250), and the inner class’s close() is an empty no-op (ImeAdapterImpl.java:264-265), so the Java side never tracks or tears down a binding. Because a single long-lived ImeAdapterImpl is reused across renderer connections, each call to bindImeRenderHost leaves the previous receiver bound and able to invoke updateCursorAnchorInfo, which is forwarded to the adapter’s updateCursorAnchorInfo (ImeAdapterImpl.java:254-258, 1774). The invariant that only the current renderer’s receiver may drive IME/cursor state is therefore not enforced.

The fix adds a mBoundImeRenderWidgetHost field (diff @@ -211), makes bindImeRenderHost close and null the prior host before binding the new one (diff @@ -1817), closes it in destroyFromNative (diff @@ -1098), and changes close() to actually call mHandle.close() (diff @@ -262) so the old pipe is torn down.

Key insight
The code never closed a previously bound ImeRenderWidgetHostImpl when a new one was created, so a stale receiver stayed live; the fix retains the binding in mBoundImeRenderWidgetHost and explicitly closes it before each rebind and on destruction.

Attack Path

  1. Hold a receiver A renderer gets an ImeRenderWidgetHost receiver bound to the shared ImeAdapterImpl via bindImeRenderHost (ImeAdapterImpl.java:1818).
  2. Survive a rebind A subsequent connection calls bindImeRenderHost again, which pre-fix creates a new impl without closing the old one, leaving the earlier receiver’s pipe open (ImeAdapterImpl.java:1822).
  3. Drive victim state Messages on the stale pipe reach updateCursorAnchorInfo and update shared IME/cursor state for the current content (ImeAdapterImpl.java:254-258, 1774). (inferred: that this maps to a cross-origin victim site)
  4. UI/accessibility effect With magnification-follows-focus, updateCursorAnchorInfo can call containerView.requestRectangleOnScreen with supplied caret bounds (ImeAdapterImpl.java:1786-1807). (inferred)

Impact Assessment

An orphaned IME Mojo receiver could continue to influence updateCursorAnchorInfo state after the adapter was rebound, allowing manipulation of cursor-anchor and magnification UI on the Android browser UI/renderer boundary. The advisory rates this high severity with no in-the-wild exploitation; the cross-origin victim scenario is the reporter’s claim and is not demonstrated by the shown Java code (inferred).

Changed Functions

FunctionChangeNotes
if
content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
modified

Files Changed

  • content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java

Audit Directions

  • Unclosed Mojo receivers on reused objects
    When a long-lived Java object rebinds a Mojo interface, verify the previous receiver is explicitly closed rather than left to garbage collection.
  • No-op lifecycle methods
    Treat empty close()/dispose implementations on interface-backed objects as suspect; confirm the underlying handle is actually released.
  • Shared per-WebContents state
    Audit paths where a persistent per-WebContents object forwards renderer-supplied data, ensuring only the current connection can mutate shared state.
From 253b422fbd1b50f3f44d22fb78a394f3e71dff10 Mon Sep 17 00:00:00 2001
From: Bo Liu <[email protected]>
Date: Mon, 18 May 2026 14:56:33 -0700
Subject: [PATCH] android: Unbind old ImeRenderWidgetHostImpl

Bug: 513995565
Change-Id: I562f024daffcd178c09c962096f580806b62e448
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7857409
Commit-Queue: Jinsuk Kim <[email protected]>
Auto-Submit: Bo Liu <[email protected]>
Reviewed-by: Jinsuk Kim <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1632423}
---

diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
index f33ce62..239f57e 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
@@ -211,6 +211,8 @@
 
     private @Nullable AutocorrectManager mAutocorrectManager;
 
+    private @Nullable ImeRenderWidgetHostImpl mBoundImeRenderWidgetHost;
+
     /**
      * {@ResultReceiver} passed in InputMethodManager#showSoftInput}. We need this to scroll to the
      * editable node at the right timing, which is after input method window shows up.
@@ -262,7 +264,9 @@
         public void onConnectionError(MojoException e) {}
 
         @Override
-        public void close() {}
+        public void close() {
+            mHandle.close();
+        }
     }
 
     /**
@@ -1098,6 +1102,11 @@
             mWebContents.getStylusWritingHandler().onImeAdapterDestroyed();
         }
 
+        if (mBoundImeRenderWidgetHost != null) {
+            mBoundImeRenderWidgetHost.close();
+            mBoundImeRenderWidgetHost = null;
+        }
+
         WeakReference<ImeAdapterImpl> oldValue = sNativeHelperMap.remove(mNativeImeAdapterAndroid);
         assert oldValue != null;
         assert oldValue.get() == this;
@@ -1817,9 +1826,13 @@
      */
     @CalledByNative
     private void bindImeRenderHost(long nativeHandle) {
+        if (mBoundImeRenderWidgetHost != null) {
+            mBoundImeRenderWidgetHost.close();
+            mBoundImeRenderWidgetHost = null;
+        }
         MessagePipeHandle handle =
                 CoreImpl.getInstance().acquireNativeHandle(nativeHandle).toMessagePipeHandle();
-        new ImeRenderWidgetHostImpl(this, handle);
+        mBoundImeRenderWidgetHost = new ImeRenderWidgetHostImpl(this, handle);
     }
 
     /**
Loading diff…

Original Bug Report

reported by [email protected]

Potential cross-origin UI manipulation via orphaned ImeRenderWidgetHost receivers on Android

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 vulnerability in the Android IME implementation allows a compromised renderer to retain a Mojo connection to a WebContents after a cross-origin navigation. By sending messages through this orphaned connection, an attacker can manipulate accessibility magnification and IME state for the new origin. This issue stems from the failure to explicitly close old Mojo receivers when binding new ones during RenderFrameHost transitions.

Affected files:

  • content/browser/android/ime_adapter_android.cc
  • content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
  • content/public/android/java/src/org/chromium/content/browser/input/CursorAnchorInfoController.java

Estimated timestamp from git blame: Unknown (Google3 checkout)

Summary

In the Android implementation of Chromium, the ImeAdapterImpl (Java) and its native counterpart ImeAdapterAndroid (C++) are persistent objects tied to the lifetime of a WebContents. During cross-origin navigations, these objects survive and reconnect to the new renderer process. However, the Mojo receiver for the blink.mojom.ImeRenderWidgetHost interface is not explicitly closed when a new connection is established. This allows a compromised renderer from a previous origin to maintain its pipe and influence the IME and accessibility state of a subsequent, cross-origin victim site.

Technical Details

When the primary main frame navigates, ImeAdapterAndroid::SetImeRenderWidgetHost is called to establish a Mojo connection with the new renderer (content/browser/android/ime_adapter_android.cc:526). This function creates a new pipe and passes the receiver to the Java-side ImeAdapterImpl.bindImeRenderHost via JNI.

On the Java side, bindImeRenderHost instantiates a new ImeRenderWidgetHostImpl and binds it to the handle (content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java:1822). In the Chromium Mojo Java implementation, the Router (the underlying controller) for the binding must be explicitly closed to terminate the pipe. Because ImeAdapterImpl does not track or close previous Router instances, the pipe remains active as long as the renderer keeps it open. A native GlobalRef in WatcherImpl prevents the Java objects from being garbage collected while the pipe is alive.

Potential Impact

A compromised renderer can exploit this persistent connection to affect a victim site after navigation:

  1. Accessibility Magnification Hijacking: An attacker can call UpdateCursorAnchorInfo with a malicious insertionMarker. If the user has accessibility magnification enabled (controlled by the AccessibilityMagnificationFollowsFocus feature), the browser will call containerView.requestRectangleOnScreen() with attacker-supplied coordinates while the user is on the victim site (ImeAdapterImpl.java:1799). This causes the magnification viewport to jump to arbitrary locations.
  2. IME UI Spoofing: The attacker can poison the mInputCursorAnchorInfo in CursorAnchorInfoController.java. This state is used by the system IME to position suggestion popups and handwriting bounds, allowing the attacker to displace these UI elements on the victim page.

Suggested Attack Steps

  1. A user navigates to an attacker-controlled site, which is compromised or malicious.
  2. The attacker’s renderer stashes the ImeRenderWidgetHost Mojo remote.
  3. The user navigates to a sensitive cross-origin site (e.g., a bank).
  4. The attacker’s renderer sends an UpdateCursorAnchorInfo message through the stashed remote.
  5. The browser process, using the orphaned receiver, processes the message and updates the shared ImeAdapterImpl state, triggering UI or accessibility changes on the victim page.

Modify ImeAdapterAndroid to maintain a handle to the blink.mojom.ImeRenderWidgetHost receiver (or its Java-side Router) and explicitly close the existing connection before binding a new one in SetImeRenderWidgetHost. Additionally, ImeRenderWidgetHostImpl in Java should implement logic to invalidate itself when the ImeAdapter is disconnected from its current RenderWidgetHostView.

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.

View on issue tracker