Low chrome Memory Corruption 🔧 Commit mapped

Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMemory corruption in ContentCapture
DescriptionMemory corruption in ContentCapture
ComponentContentCapture
Sub-componentContentCapture
Bug ClassMemory Corruption
Tracker535725111
Fix commitc88830605dc4 (chromium/src) +92/-2
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-20

Changed Functions

FunctionChangeNotes
for
components/content_capture/android/java/src/org/chromium/components/content_capture/PlatformContentCaptureController.java
modified

Files Changed

  • components/content_capture/android/junit/src/org/chromium/components/content_capture/PlatformContentCaptureControllerTest.java
  • components/content_capture/android/java/src/org/chromium/components/content_capture/PlatformContentCaptureController.java
From c88830605dc40c8509f5664250862efe1b2761d2 Mon Sep 17 00:00:00 2001
From: Peilin Wang <[email protected]>
Date: Wed, 16 Sep 2026 07:51:37 -0700
Subject: [PATCH] [ContentCapture] Don't send deletion urls to unauthorized services

We should only be sending deletion urls if we used content capture. If
we didn't use content capture, the deletion urls should stay within
chrome. This CL adds a check to pass the deletion urls only if we used
content capture.

Bug: 535725111
Change-Id: I844dd527fe52f56fe9917f2f7d11e4a5fbd43834
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8415045
Commit-Queue: Peilin Wang <[email protected]>
Reviewed-by: Sayed Elabady <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1698932}
---
diff --git a/components/content_capture/android/java/src/org/chromium/components/content_capture/PlatformContentCaptureController.java b/components/content_capture/android/java/src/org/chromium/components/content_capture/PlatformContentCaptureController.java
index a21fe1f..1413009 100644
--- a/components/content_capture/android/java/src/org/chromium/components/content_capture/PlatformContentCaptureController.java
+++ b/components/content_capture/android/java/src/org/chromium/components/content_capture/PlatformContentCaptureController.java
@@ -140,14 +140,34 @@
         Log.i(TAG, msg);
     }
 
+    /**
+     * Whether data removal requests may be sent to the current ContentCaptureService.
+     *
+     * <p>A removal request names the data to delete, and {@link #clearContentCaptureDataForURLs}
+     * puts the full spec of every deleted URL into it. It must therefore only be sent to a service
+     * that could have received captured content in the first place; sending it to any other service
+     * would disclose the user's browsing history to a service that never had it, with no data to
+     * delete in return.
+     *
+     * <p>This is deliberately weaker than {@link #shouldStartCapture()}. AiAi may still hold
+     * content captured while content capture was enabled, so it has to keep receiving deletions
+     * after capture is turned off, otherwise deleting history would silently leave the captured
+     * copy behind. {@code mShouldStartCapture} additionally covers debug and dump-for-testing
+     * builds, where a non-AiAi service is intentionally allowed to capture.
+     */
+    private boolean canRemoveData() {
+        if (mContentCaptureManager == null) return false;
+        return mIsAiai || mShouldStartCapture;
+    }
+
     public void clearAllContentCaptureData() {
-        if (mContentCaptureManager == null) return;
+        if (!canRemoveData()) return;
 
         mContentCaptureManager.removeData(new DataRemovalRequest.Builder().forEverything().build());
     }
 
     public void clearContentCaptureDataForURLs(String[] urlsToDelete) {
-        if (mContentCaptureManager == null) return;
+        if (!canRemoveData()) return;
 
         DataRemovalRequest.Builder builder = new DataRemovalRequest.Builder();
         for (String url : urlsToDelete) {
diff --git a/components/content_capture/android/junit/src/org/chromium/components/content_capture/PlatformContentCaptureControllerTest.java b/components/content_capture/android/junit/src/org/chromium/components/content_capture/PlatformContentCaptureControllerTest.java
index 92298a8..5fecc63 100644
--- a/components/content_capture/android/junit/src/org/chromium/components/content_capture/PlatformContentCaptureControllerTest.java
+++ b/components/content_capture/android/junit/src/org/chromium/components/content_capture/PlatformContentCaptureControllerTest.java
@@ -4,9 +4,13 @@
 
 package org.chromium.components.content_capture;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -14,17 +18,21 @@
 import android.os.Build;
 import android.view.contentcapture.ContentCaptureCondition;
 import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.DataRemovalRequest;
 
 import androidx.annotation.RequiresApi;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
@@ -35,13 +43,22 @@
 @Config(sdk = BaseRobolectricTestRunner.MIN_SDK)
 @RequiresApi(Build.VERSION_CODES.Q)
 public class PlatformContentCaptureControllerTest {
+    private static final String SENSITIVE_URL = "https://example.com/account?session_token=secret";
+
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
     private ContentCaptureManager mContentCaptureManager;
     private Context mContext;
     private ComponentName mComponentName;
+    private String mOriginalBuildType;
 
     @Before
     public void setUp() {
+        // verifyService() lets a non-AiAi service through on debuggable builds; pin the build type
+        // so that the tests below exercise the production behavior regardless of the build type
+        // baked into the Robolectric android-all jar.
+        mOriginalBuildType = Build.TYPE;
+        ReflectionHelpers.setStaticField(Build.class, "TYPE", "user");
+
         mContext = Mockito.mock(Context.class);
         mContentCaptureManager = Mockito.mock(ContentCaptureManager.class);
         mComponentName = Mockito.mock(ComponentName.class);
@@ -54,6 +71,11 @@
         doReturn(null).when(mContentCaptureManager).getContentCaptureConditions();
     }
 
+    @After
+    public void tearDown() {
+        ReflectionHelpers.setStaticField(Build.class, "TYPE", mOriginalBuildType);
+    }
+
     @Test
     public void testEverythingAllowed() throws Throwable {
         PlatformContentCaptureController controller =
@@ -105,4 +127,52 @@
                 new PlatformContentCaptureController(mContext);
         assertFalse(controller.shouldStartCapture());
     }
+
+    @Test
+    public void testNoDataRemovalRequestSentToNonAiaiService() throws Throwable {
+        doReturn("org.abc").when(mComponentName).getPackageName();
+        PlatformContentCaptureController controller =
+                new PlatformContentCaptureController(mContext);
+        assertFalse(controller.isAiai());
+        assertFalse(controller.shouldStartCapture());
+
+        controller.clearContentCaptureDataForURLs(new String[] {SENSITIVE_URL});
+        controller.clearAllContentCaptureData();
+
+        // The service never received any content, so there is nothing for it to delete; sending it
+        // the deleted URLs would only disclose the user's browsing history to it.
+        verify(mContentCaptureManager, never()).removeData(any(DataRemovalRequest.class));
+    }
+
+    @Test
+    public void testDataRemovalRequestSentToAiaiService() throws Throwable {
+        PlatformContentCaptureController controller =
+                new PlatformContentCaptureController(mContext);
+        assertTrue(controller.isAiai());
+
+        controller.clearContentCaptureDataForURLs(new String[] {SENSITIVE_URL});
+
+        ArgumentCaptor<DataRemovalRequest> captor =
+                ArgumentCaptor.forClass(DataRemovalRequest.class);
+        verify(mContentCaptureManager).removeData(captor.capture());
+        assertEquals(1, captor.getValue().getLocusIdRequests().size());
+        assertEquals(
+                SENSITIVE_URL, captor.getValue().getLocusIdRequests().get(0).getLocusId().getId());
+    }
+
+    @Test
+    public void testDataRemovalRequestSentToAiaiServiceWhenCaptureDisabled() throws Throwable {
+        // AiAi may still hold content captured before content capture was turned off, so deletions
+        // must keep flowing even though no new content is being captured.
+        doReturn(false).when(mContentCaptureManager).isContentCaptureEnabled();
+        PlatformContentCaptureController controller =
+                new PlatformContentCaptureController(mContext);
+        assertTrue(controller.isAiai());
+        assertFalse(controller.shouldStartCapture());
+
+        controller.clearContentCaptureDataForURLs(new String[] {SENSITIVE_URL});
+        controller.clearAllContentCaptureData();
+
+        verify(mContentCaptureManager, Mockito.times(2)).removeData(any(DataRemovalRequest.class));
+    }
 }
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.