Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient data validation in Spellcheck
DescriptionInsufficient data validation in Spellcheck
ComponentSpellcheck
Bug ClassLogic Error
Tracker507239830
Fix commit6c7b3e31db18 (chromium/src) +5/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Files Changed

  • chrome/browser/spellchecker/spellcheck_service.cc
From 6c7b3e31db1852b83d93d4fb80f94d96a367a4d6 Mon Sep 17 00:00:00 2001
From: Avi Drissman <avi@chromium.org>
Date: Wed, 06 May 2026 09:17:58 -0700
Subject: [PATCH] Only send dictionary updates to correct render processes

When updating the custom dictionary, only send the updates to render
processes that belong to the correct profile.

Fixed: 507239830
Change-Id: Ie535a8c98b866bd5349b6df2ace836b86a6a6964
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7812281
Reviewed-by: Chuong Ho <hdchuong@chromium.org>
Commit-Queue: Avi Drissman <avi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1626232}
---

diff --git a/chrome/browser/spellchecker/spellcheck_service.cc b/chrome/browser/spellchecker/spellcheck_service.cc
index ad11f48..3dc2308 100644
--- a/chrome/browser/spellchecker/spellcheck_service.cc
+++ b/chrome/browser/spellchecker/spellcheck_service.cc
@@ -548,8 +548,12 @@
   for (auto it = content::RenderProcessHost::AllHostsIterator(); !it.IsAtEnd();
        it.Advance()) {
     content::RenderProcessHost* process = it.GetCurrentValue();
-    if (!process->IsInitializedAndNotDead())
+    if (!process->IsInitializedAndNotDead() ||
+        SpellcheckServiceFactory::GetForContext(process->GetBrowserContext()) !=
+            this) {
       continue;
+    }
+
     GetSpellCheckerForProcess(process)->CustomDictionaryChanged(additions,
                                                                 deletions);
   }
Loading diff…

Original Bug Report

reported by vm...@google.com

Cross-profile leak of custom dictionary words via unguarded AllHostsIterator()

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 without the Chrome Security team. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: A logic flaw in SpellcheckService causes custom dictionary updates to be broadcast globally to all renderer processes without verifying the profile. This potentially leaks newly added sensitive dictionary words from one profile to renderers belonging to other profiles. An attacker could extract this cross-profile data via a compromised renderer or a spellcheck oracle side-channel.

Affected files:

  • chrome/browser/spellchecker/spellcheck_service.cc
  • chrome/browser/spellchecker/spellcheck_custom_dictionary.cc
  • chrome/browser/renderer_context_menu/spelling_menu_observer.cc

Estimated timestamp from git blame: 2019-08-01

A potential cross-profile data leak exists in SpellcheckService::OnCustomDictionaryChanged, where custom dictionary additions and deletions are incorrectly broadcast to all active renderer processes across all BrowserContexts, exposing sensitive user data to unrelated profiles.

Initial logic and parameters involving the user interface adding a word to the SpellcheckCustomDictionary, updating the internal structures, and notifying the SpellcheckService via observer callbacks are validated and function as expected.

However, the critical flaw manifests directly in the broadcast phase. In SpellcheckService::OnCustomDictionaryChanged, the code utilizes content::RenderProcessHost::AllHostsIterator() to loop through renderers. It jumps directly to transmitting the plaintext sensitive word via the spellcheck::mojom::SpellChecker::CustomDictionaryChanged Mojo message to every active renderer. There is no verification that the RenderProcessHost belongs to the same BrowserContext as the SpellcheckService. Consequently, words added in a “Personal” profile are immediately synchronized into the renderer state of completely separate profiles (e.g., “Work” or “Guest”).

Potential Attacker Steps (Tooling constrained - no PoC execution)

  1. An attacker controls a webpage that the victim visits using Profile B.
  2. The victim, in a separate window using Profile A, adds a sensitive word to their custom spellcheck dictionary.
  3. The browser process unconditionally broadcasts the CustomDictionaryChanged Mojo IPC containing the plaintext word to the attacker’s renderer process in Profile B.
  4. The attacker extracts the leaked word either by directly reading renderer memory (if the renderer is compromised) or by utilizing a spellcheck oracle side-channel (dynamically probing words to observe if the spellchecker flags them as incorrect).

Suggested Fix

Modify SpellcheckService::OnCustomDictionaryChanged to filter renderers by BrowserContext before dispatching the Mojo message. For example:

content::BrowserContext* service_context = ...; // Get the context associated with this SpellcheckService
for (content::RenderProcessHost::iterator it(
         content::RenderProcessHost::AllHostsIterator());
     !it.IsAtEnd(); it.Advance()) {
  content::RenderProcessHost* process = it.GetCurrentValue();
  if (!process->IsInitializedAndNotDead() || 
      process->GetBrowserContext() != service_context) {
    continue;
  }
  GetSpellCheckerForProcess(process)->CustomDictionaryChanged(additions, deletions);
}

Evaluated with Chrome root at commit: a1e33f5848218e21d4a16ae2c1bc94e815c30c7f


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. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker