Critical chrome Uninitialized Memory 📄 Reporter bug report 🔧 Commit mapped

Overview

Critical
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse of uninitialized variable in Mobile
DescriptionUse of uninitialized variable in Mobile
ComponentMobile
Bug ClassUninitialized Memory
Tracker522082472
Fix commitd86ba31be3c1 (chromium/src) +6/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-08-25

Changed Functions

FunctionChangeNotes
GetLensOverlaySuggestInputs
ios/chrome/browser/composebox/coordinator/composebox_cobrowse_omnibox_client.mm
modified
if
ios/chrome/browser/composebox/coordinator/composebox_cobrowse_omnibox_client.mm
modified
GetLensOverlaySuggestInputs
ios/chrome/browser/composebox/coordinator/composebox_omnibox_client.mm
modified
if
ios/chrome/browser/composebox/coordinator/composebox_omnibox_client.mm
modified

Files Changed

  • ios/chrome/browser/composebox/coordinator/composebox_cobrowse_omnibox_client.mm
  • ios/chrome/browser/composebox/coordinator/composebox_omnibox_client.mm
From d86ba31be3c13f0e85c4d34150fe0b0964711cf6 Mon Sep 17 00:00:00 2001
From: Stepan Khapugin <[email protected]>
Date: Thu, 02 Jul 2026 09:41:35 -0700
Subject: [PATCH] [iOS] Return std::nullopt when necessary.

When delegate_ is nil, std::optional will not do the right thing.

Fixed: 522082472
Change-Id: Ie9e975b5c593aeed6b2e2b2b094e8a25200b4174
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8034007
Commit-Queue: Radu Nitescu <[email protected]>
Auto-Submit: Stepan Khapugin <[email protected]>
Commit-Queue: Stepan Khapugin <[email protected]>
Reviewed-by: Radu Nitescu <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1656148}
---

diff --git a/ios/chrome/browser/composebox/coordinator/composebox_cobrowse_omnibox_client.mm b/ios/chrome/browser/composebox/coordinator/composebox_cobrowse_omnibox_client.mm
index 453a3aae..e5e9391 100644
--- a/ios/chrome/browser/composebox/coordinator/composebox_cobrowse_omnibox_client.mm
+++ b/ios/chrome/browser/composebox/coordinator/composebox_cobrowse_omnibox_client.mm
@@ -176,6 +176,9 @@
 
 std::optional<lens::proto::LensOverlaySuggestInputs>
 ComposeboxCobrowseOmniboxClient::GetLensOverlaySuggestInputs() const {
+  if (!delegate_) {
+    return std::nullopt;
+  }
   return [delegate_ suggestInputs];
 }
 
diff --git a/ios/chrome/browser/composebox/coordinator/composebox_omnibox_client.mm b/ios/chrome/browser/composebox/coordinator/composebox_omnibox_client.mm
index dbc93d09..e7b8c97 100644
--- a/ios/chrome/browser/composebox/coordinator/composebox_omnibox_client.mm
+++ b/ios/chrome/browser/composebox/coordinator/composebox_omnibox_client.mm
@@ -169,6 +169,9 @@
 
 std::optional<lens::proto::LensOverlaySuggestInputs>
 ComposeboxOmniboxClient::GetLensOverlaySuggestInputs() const {
+  if (!delegate_) {
+    return std::nullopt;
+  }
   return [delegate_ suggestInputs];
 }
 
Loading diff…

Original Bug Report

reported by [email protected]

Potential Use-of-Uninitialized-Memory in Composebox via nil delegate message

Flapjack, 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 potential use-of-uninitialized-memory vulnerability exists in the Composebox feature on iOS ARM64. Messaging a nil delegate to return a large C++ struct leaves the return buffer uninitialized due to SRet calling conventions, leading to a potential arbitrary free primitive when the resulting std::optional is destroyed.

Affected files:

  • ios/chrome/browser/composebox/coordinator/composebox_omnibox_client.mm
  • ios/chrome/browser/composebox/coordinator/composebox_cobrowse_omnibox_client.mm

Estimated timestamp from git blame: 2025-11-18

Summary

A potential use-of-uninitialized-memory vulnerability exists in ComposeboxOmniboxClient and ComposeboxCobrowseOmniboxClient on iOS ARM64. The vulnerability is triggered when an Objective-C message is sent to a nil receiver (delegate_) that is expected to return a C++ struct larger than 16 bytes.

Due to the ARM64 SRet (Struct Return) calling convention and objc_msgSend behavior, messaging a nil receiver leaves the caller-allocated return buffer completely uninitialized. When the returned std::optional object is subsequently destroyed, it may attempt to free attacker-controlled pointers from the stack, resulting in an arbitrary free primitive and potential Remote Code Execution (RCE) in the browser process.

Technical Details

In ios/chrome/browser/composebox/coordinator/composebox_omnibox_client.mm and ios/chrome/browser/composebox/coordinator/composebox_cobrowse_omnibox_client.mm, the methods GetInputState and GetLensOverlaySuggestInputs call methods on a __weak delegate (delegate_):

// ios/chrome/browser/composebox/coordinator/composebox_omnibox_client.mm
omnibox::InputState ComposeboxOmniboxClient::GetInputState() const {
  std::optional<contextual_search::InputState> state = [delegate_ inputState];
  return state.value_or(omnibox::InputState());
}

std::optional<lens::proto::LensOverlaySuggestInputs>
ComposeboxOmniboxClient::GetLensOverlaySuggestInputs() const {
  return [delegate_ suggestInputs];
}

The return types std::optional<contextual_search::InputState> (which aliases omnibox::InputState) and std::optional<lens::proto::LensOverlaySuggestInputs> are both C++ structs significantly larger than 16 bytes.

On the ARM64 architecture, the calling convention for functions returning a struct larger than 16 bytes requires the caller to allocate a return buffer on the stack and pass a pointer to it (the SRet pointer). If an Objective-C message is sent to a nil receiver expecting an SRet return, the runtime (objc_msgSend) returns immediately without initializing or zeroing the memory pointed to by the SRet pointer.

During UI teardown in ComposeboxInputPlateCoordinator::stop(), the _mediator (which acts as the delegate) is explicitly set to nil before the _omniboxCoordinator and its _client are stopped. If an autocomplete request or UI event synchronously queries GetInputState() or GetLensOverlaySuggestInputs() during this window, the C++ std::optional variable will contain uninitialized stack memory.

Impact

When the std::optional object goes out of scope and is destroyed, its destructor checks its boolean has_value flag. Because the memory is uninitialized, this flag may evaluate to true based on residual stack data. If so, the destructor of the underlying struct will be invoked on uninitialized memory.

Both contextual_search::InputState and lens::proto::LensOverlaySuggestInputs contain complex heap-managed members (e.g., std::vector, std::map, std::string, and Protobuf objects). Their destructors will attempt to call free() or delete on pointers read directly from the uninitialized stack buffer.

Potential Exploitation Steps

(Note: These are suggested theoretical steps as our tooling agent cannot execute dynamic proof-of-concepts)

  1. An attacker directs the user to a malicious webpage.
  2. The attacker uses JavaScript or WebAssembly to “spray” the browser process’s main thread stack with crafted data. This data includes fake heap pointers and a specific byte pattern to ensure the has_value flag of the std::optional is set to true.
  3. The user interacts with the Composebox omnibox.
  4. A UI teardown is triggered (e.g., via navigation or user interaction).
  5. ComposeboxInputPlateCoordinator::stop() is called. _mediator is set to nil, making the weak delegate_ pointer nil.
  6. A synchronous UI or autocomplete event triggers _omniboxClient->GetInputState().
  7. objc_msgSend returns immediately because delegate_ is nil, leaving the SRet buffer uninitialized and filled with the attacker’s sprayed stack data.
  8. The std::optional is destructed, reading the fake has_value flag and subsequently freeing the attacker-controlled fake pointers.
  9. This arbitrary free primitive is leveraged to corrupt heap metadata or free a targeted object, leading to full RCE and sandbox escape within the browser process.

Explicitly check if delegate_ is nil before sending the message, and return an empty std::optional or default-constructed object to prevent the Objective-C runtime from leaving the SRet buffer uninitialized:

omnibox::InputState ComposeboxOmniboxClient::GetInputState() const {
  if (!delegate_) {
    return omnibox::InputState();
  }
  std::optional<contextual_search::InputState> state = [delegate_ inputState];
  return state.value_or(omnibox::InputState());
}

std::optional<lens::proto::LensOverlaySuggestInputs>
ComposeboxOmniboxClient::GetLensOverlaySuggestInputs() const {
  if (!delegate_) {
    return std::nullopt;
  }
  return [delegate_ suggestInputs];
}

Similar checks should be applied in ComposeboxCobrowseOmniboxClient.

Evaluated with Chrome root at commit: 2155cb00003ec35716a76ed3246eae995f87b7ff


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