CVE-2026-79198
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/blink/renderer/platform/widget/widget_base.cc |
modified |
Files Changed
third_party/blink/renderer/platform/widget/widget_base.cc
Patch
From c092d5682b44e65442a5d1c1d8b5c4d88e13ead0 Mon Sep 17 00:00:00 2001 From: Dave Tapuska <[email protected]> Date: Fri, 24 Jul 2026 09:47:27 -0700 Subject: [PATCH] Fix potential use-after-free in WidgetBase. The call to frame_widget->TextInputInfo() can trigger synchronous events that destroy the WidgetBase instance. This CL adds a weak pointer check to safely return early if WidgetBase is destroyed during the call. BUG=536606137 Change-Id: I608949df84c68980760d8c068ddefd8edca5fe4e Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8139142 Reviewed-by: David Bokan <[email protected]> Commit-Queue: Dave Tapuska <[email protected]> Cr-Commit-Position: refs/heads/main@{#1667929} --- diff --git a/third_party/blink/renderer/platform/widget/widget_base.cc b/third_party/blink/renderer/platform/widget/widget_base.cc index 05a9cb7..b50876b 100644 --- a/third_party/blink/renderer/platform/widget/widget_base.cc +++ b/third_party/blink/renderer/platform/widget/widget_base.cc @@ -1270,7 +1270,11 @@ std::optional<gfx::Rect> control_bounds; std::optional<gfx::Rect> selection_bounds; if (frame_widget) { + base::WeakPtr<WidgetBase> weak_this = weak_ptr_factory_.GetWeakPtr(); new_info = frame_widget->TextInputInfo(); + if (!weak_this) { + return; + } // This will be used to decide whether or not to show VK when VK policy is // manual. last_vk_visibility_request =
Original Bug Report
Potential Use-After-Free in WidgetBase::UpdateTextInputStateInternal via synchronous layout
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 potential Use-After-Free (UAF) vulnerability exists in WidgetBase::UpdateTextInputStateInternal because a synchronous layout update can be forced during text input retrieval, allowing script execution to destroy the WidgetBase instance before subsequent member accesses. This can lead to arbitrary code execution in the sandboxed renderer process.
Affected files:
third_party/blink/renderer/platform/widget/widget_base.cc
Estimated timestamp from git blame: 2020-06-19
Root Cause
In third_party/blink/renderer/platform/widget/widget_base.cc, the method WidgetBase::UpdateTextInputStateInternal is responsible for updating the text input state of the widget:
void WidgetBase::UpdateTextInputStateInternal(bool show_virtual_keyboard,
bool reply_to_request) {
...
FrameWidget* frame_widget = client_->FrameWidget();
...
if (frame_widget) {
new_info = frame_widget->TextInputInfo(); // [1] can force synchronous style and layout
last_vk_visibility_request =
frame_widget->GetLastVirtualKeyboardVisibilityRequest();
frame_widget->GetEditContextBoundsInWindow(&control_bounds,
&selection_bounds);
}
...
bool new_can_compose_inline = CanComposeInline(); // [2] reads freed this->client_ and performs a virtual call
if (show_virtual_keyboard || reply_to_request ||
text_input_type_ != new_type || ... ) { // accesses other freed members
...
widget_host_->TextInputStateChanged(std::move(params)); // [3] accesses freed AssociatedRemote
text_input_info_ = new_info; // [4] UAF writes
...
}
}
At [1], frame_widget->TextInputInfo() is called, which ultimately forces a synchronous style and layout update via InputMethodController::TextInputInfo():
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
During this layout update, if any plugin element is detached (e.g. by setting display: none in CSS or toggling a class), the plugin is queued for deferred disposal via PluginDisposeSuspendScope. Upon completing layout calculation, the scope’s destructor triggers PerformDeferredPluginDispose(), which calls WebPluginContainerImpl::Dispose(). Inside Dispose(), web_plugin_->Destroy() is called within ScriptForbiddenScope::AllowUserAgentScript allow_script, allowing synchronous JavaScript execution.
Under this script window, an attacker’s script could synchronously detach the local root frame hosting the widget. Doing so invokes WebFrameWidgetImpl::Close(), which resets the std::unique_ptr<WidgetBase> widget_base_ member, immediately freeing the WidgetBase instance.
Upon returning from style and layout recalculation, execution in UpdateTextInputStateInternal resumes with a dangling this pointer. When CanComposeInline() at [2] is called, it accesses the freed client_ pointer and performs a virtual call (client_->FrameWidget()). If the attacker reclaims the freed allocation with controlled bytes (e.g., via heap grooming or spraying during the script window), this virtual call allows control-flow hijacking and potential Remote Code Execution (RCE) in the renderer process. Additionally, subsequent lines execute UAF writes ([4]) into the freed memory block.
A sibling method in the same file, UpdateCompositionInfo, was previously patched for this identical layout-forcing destruction pattern by introducing a base::WeakPtr check, but UpdateTextInputStateInternal remains unguarded.
Potential Reproduction Steps
Note: These are potential, theoretical reproduction steps as our tooling does not currently have the capability to execute proof-of-concept exploits.
- Serve a page with a subframe local root (e.g., an OOPIF or fenced frame) so that detaching the subframe will call
WebFrameWidgetImpl::Close(). - Inside the subframe, insert an editable node (e.g.
<div contenteditable="true">) and an<embed>/<object>element hosting a plugin that allows script execution on destruction. - Configure a CSS rule on the plugin element that will detach its layout object when layout is dirty.
- Call
focus()on the editable element to triggerWidgetBase::UpdateTextInputStateInternaland force synchronous layout. - In the deferred plugin disposal callback, execute JavaScript that synchronously removes the subframe from the parent document.
- Perform heap grooming/spraying during the script window to reclaim the
WidgetBaseallocation block and overwrite the offset ofclient_with a crafted pointer. - Observe a control-flow hijack upon resumption when the virtual function call
client_->FrameWidget()insideCanComposeInline()is executed on the freedWidgetBase.
Suggested Fix
To remediate this issue, introduce a liveness check using WeakPtr<WidgetBase> immediately after the synchronous layout-forcing call, similar to the fix applied to the sibling UpdateCompositionInfo method:
if (frame_widget) {
base::WeakPtr<WidgetBase> weak_this = weak_ptr_factory_.GetWeakPtr();
new_info = frame_widget->TextInputInfo();
if (!weak_this) {
return;
}
last_vk_visibility_request =
frame_widget->GetLastVirtualKeyboardVisibilityRequest();
frame_widget->GetEditContextBoundsInWindow(&control_bounds,
&selection_bounds);
}
This ensures that if the WidgetBase instance is destroyed during the style and layout recalculation phase, execution halts and early-returns safely before any subsequent member accesses or virtual method calls occur.
Evaluated with Chrome root at commit: bf775e5d75cb9e1767e2cd02cc93efa0077d14a5
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.