CVE-2026-8574
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifcomponents/embedder_support/android/java/src/org/chromium/components/embedder_support/delegate/ColorPickerBridge.java |
modified |
Files Changed
components/embedder_support/android/delegate/color_picker_bridge.cccomponents/embedder_support/android/java/src/org/chromium/components/embedder_support/delegate/ColorPickerBridge.java
Patch
From f07a64bbbbb1f4b499ae000b301bc4714e463ea6 Mon Sep 17 00:00:00 2001
From: Ted Choc <tedchoc@chromium.org>
Date: Wed, 25 Mar 2026 09:28:06 -0700
Subject: [PATCH] Ensure native ptr held in Java is cleared when ColorPickerBridge is deleted.
Bug: 495902113
Change-Id: Id320c0b9f7f2b4c3e835a63ccb941d40d6eeab63
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7698805
Commit-Queue: Ted Choc <tedchoc@chromium.org>
Reviewed-by: Bo Liu <boliu@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1604888}
---
diff --git a/components/embedder_support/android/delegate/color_picker_bridge.cc b/components/embedder_support/android/delegate/color_picker_bridge.cc
index 18d7274..66042cde 100644
--- a/components/embedder_support/android/delegate/color_picker_bridge.cc
+++ b/components/embedder_support/android/delegate/color_picker_bridge.cc
@@ -52,7 +52,12 @@
Java_ColorPickerBridge_showColorPicker(env, j_color_chooser_, initial_color);
}
-ColorPickerBridge::~ColorPickerBridge() = default;
+ColorPickerBridge::~ColorPickerBridge() {
+ if (!j_color_chooser_.is_null()) {
+ JNIEnv* env = AttachCurrentThread();
+ Java_ColorPickerBridge_detach(env, j_color_chooser_);
+ }
+}
void ColorPickerBridge::End() {
if (!j_color_chooser_.is_null()) {
diff --git a/components/embedder_support/android/java/src/org/chromium/components/embedder_support/delegate/ColorPickerBridge.java b/components/embedder_support/android/java/src/org/chromium/components/embedder_support/delegate/ColorPickerBridge.java
index 7df093e0..fa66f0f 100644
--- a/components/embedder_support/android/java/src/org/chromium/components/embedder_support/delegate/ColorPickerBridge.java
+++ b/components/embedder_support/android/java/src/org/chromium/components/embedder_support/delegate/ColorPickerBridge.java
@@ -27,25 +27,30 @@
@JNINamespace("web_contents_delegate_android")
@NullMarked
public class ColorPickerBridge {
- private final long mNativeDialog;
+ private long mNativeColorPicker;
private final ColorPickerCoordinator mColorPickerCoordinator;
@CalledByNative
static @Nullable ColorPickerBridge create(
- long nativeDialog, @JniType("ui::WindowAndroid*") WindowAndroid windowAndroid) {
+ long nativeColorPicker, @JniType("ui::WindowAndroid*") WindowAndroid windowAndroid) {
if (windowAndroid == null) return null;
Context context = windowAndroid.getContext().get();
if (ContextUtils.activityFromContext(context) == null) return null;
assumeNonNull(context);
- return new ColorPickerBridge(nativeDialog, context);
+ return new ColorPickerBridge(nativeColorPicker, context);
}
- private ColorPickerBridge(long nativeDialog, Context context) {
- mNativeDialog = nativeDialog;
+ private ColorPickerBridge(long nativeColorPicker, Context context) {
+ mNativeColorPicker = nativeColorPicker;
mColorPickerCoordinator = ColorPickerCoordinator.create(context, this::onDialogDismissed);
}
@CalledByNative
+ private void detach() {
+ mNativeColorPicker = 0;
+ }
+
+ @CalledByNative
void showColorPicker(int initialColor) {
mColorPickerCoordinator.show(initialColor);
}
@@ -63,7 +68,9 @@
}
void onDialogDismissed(int newColor) {
- ColorPickerBridgeJni.get().onColorChosen(mNativeDialog, newColor);
+ if (mNativeColorPicker != 0) {
+ ColorPickerBridgeJni.get().onColorChosen(mNativeColorPicker, newColor);
+ }
}
@NativeMethods
Original Bug Report
Potential UAF in ColorPickerBridge via asynchronous dialog dismissal race
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: A potential Use-After-Free (UAF) vulnerability exists in the Android Browser process. A race condition between the asynchronous dismissal of the Android color picker dialog and the synchronous destruction of the native ColorPickerBridge object can leave a dangling C++ pointer in the Java implementation, which is subsequently dereferenced.
Affected files:
components/embedder_support/android/delegate/color_picker_bridge.cccomponents/embedder_support/android/java/src/org/chromium/components/embedder_support/delegate/ColorPickerBridge.javacontent/browser/web_contents/web_contents_impl.cccomponents/embedder_support/android/delegate/color_picker_bridge.h
Estimated timestamp from git blame: 2026-01-13
Description
A potential Use-After-Free (UAF) vulnerability exists in the Android Browser process due to a race condition between the asynchronous dismissal of an Android AlertDialog and the renderer-initiated destruction of the ColorPickerBridge object.
In the Android implementation of the color picker, ColorPickerBridge.java stores a native pointer to its corresponding C++ object in a final long mNativeDialog field (line 30). When a user cancels the dialog (e.g., by pressing the Back button), Android’s Dialog.cancel() enqueues an OnCancelListener callback as a message in the main Looper. This callback is processed asynchronously on the UI thread.
Root Cause
A compromised renderer can exploit the window between the dialog dismissal and the execution of the asynchronous callback by triggering the destruction of the ColorPickerBridge.
A renderer can repeatedly call blink::mojom::ColorChooserFactory::OpenColorChooser. In WebContentsImpl::OpenColorChooser, the call to color_chooser_holder_.reset() unconditionally destroys the previous chooser and its associated native ColorPickerBridge object.
When the native ColorPickerBridge is destroyed, its memory is freed. However, the Java-side mNativeDialog pointer is not invalidated and continues to point to the now-freed memory.
Potential Exploitation Scenario
These are suggested steps, as this AI agent does not yet have the ability to run code to confirm a working exploit.
- A compromised renderer opens a color picker dialog.
- The user dismisses the dialog (e.g., via the Back key). The
OnCancelListenermessage is enqueued to the main Looper. - The renderer immediately spams
OpenColorChooserMojo messages. One of these is processed before the dismissal message, causingWebContentsImplto reset the current chooser and free the nativeColorPickerBridgememory. - The attacker performs a heap spray to reclaim the freed memory (approximately 32 bytes) and places a controlled value at the offset of the
web_contents_field. - The queued Java cancel-listener finally fires, calling
onDialogDismissed(line 65), which invokes the JNI methodonColorChosen(mNativeDialog, ...). - The native JNI stub calls
OnColorChosenusing the stalemNativeDialogpointer. ColorPickerBridge::OnColorChosenreads the attacker-controlledweb_contents_value and performs a virtual call (DidChooseColorInColorChooser) through an attacker-controlled vtable. This could lead to arbitrary code execution (RCE) in the browser process.
Mitigation Analysis
This issue is not mitigated by MiraclePtr (BackupRefPtr). The UAF occurs on the ColorPickerBridge object itself (the “container”). The raw_ptr<WebContents> web_contents_ field resides within the freed memory. Because the attacker-controlled data is loaded as the raw_ptr value before the virtual call dispatch, the internal reference counting and quarantine mechanisms that protect the pointee of a raw_ptr are bypassed if the attacker points the fake pointer outside the BRP pool.
Suggested Fix
Add a clearNativePtr() method to the Java ColorPickerBridge class that sets mNativeDialog to 0. Call this method from the C++ ColorPickerBridge destructor via JNI. Additionally, check if mNativeDialog is non-zero before invoking JNI methods in Java.
Evaluated with Chrome root at commit: 0eb4855bda702feaaa8b899336664f97e3df88b8
Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. Please feel free to reach out to me if you have concerns or feedback.