CVE-2026-16805
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/blink/renderer/platform/widget/widget_base.cc |
modified |
Files Changed
third_party/blink/renderer/core/editing/frame_selection.ccthird_party/blink/renderer/platform/widget/widget_base.cc
Patch
From 9fb841bf28c373fccf20a546545ed581e45031d7 Mon Sep 17 00:00:00 2001
From: Koji Ishii <kojii@chromium.org>
Date: Fri, 17 Jul 2026 06:41:19 -0700
Subject: [PATCH] Add validity checks after `CompositionRange()`
Following up crrev.com/c/7840643, this patch adds validity
checks after calls to `CompositionRange()`, which invokes
`Document::UpdateStyleAndLayout()`.
Also change `FrameSelection::RootEditableElementOrDocumentElement()`
to return `nullptr` when the selection is no longer available
after the layout it forces, instead of dereferencing a cleared
`document_`.
Bug: 523292588
Change-Id: I4a82a3b57fb00137dd45be946fd8e045ed46be2f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8106916
Reviewed-by: Dave Tapuska <dtapuska@chromium.org>
Auto-Submit: Koji Ishii <kojii@chromium.org>
Commit-Queue: Dave Tapuska <dtapuska@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1663857}
---
diff --git a/third_party/blink/renderer/core/editing/frame_selection.cc b/third_party/blink/renderer/core/editing/frame_selection.cc
index 175c5a8a..96220191 100644
--- a/third_party/blink/renderer/core/editing/frame_selection.cc
+++ b/third_party/blink/renderer/core/editing/frame_selection.cc
@@ -152,9 +152,15 @@
}
Element* FrameSelection::RootEditableElementOrDocumentElement() const {
+ if (!IsAvailable()) {
+ return nullptr;
+ }
// TODO(editing-dev): The use of UpdateStyleAndLayout
// needs to be audited. See http://crbug.com/590369 for more details.
GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kSelection);
+ if (!IsAvailable()) {
+ return nullptr;
+ }
Element* selection_root =
ComputeVisibleSelectionInDomTree().RootEditableElement();
diff --git a/third_party/blink/renderer/platform/widget/widget_base.cc b/third_party/blink/renderer/platform/widget/widget_base.cc
index fd4d3bb5..3023c5c9 100644
--- a/third_party/blink/renderer/platform/widget/widget_base.cc
+++ b/third_party/blink/renderer/platform/widget/widget_base.cc
@@ -1449,8 +1449,15 @@
// Composition information is only available on editable node.
range = gfx::Range::InvalidRange();
} else {
+ base::WeakPtr<WidgetBase> weak_this = weak_ptr_factory_.GetWeakPtr();
GetCompositionRange(&range);
+ if (!weak_this) {
+ return;
+ }
GetCompositionCharacterBounds(&character_bounds);
+ if (!weak_this) {
+ return;
+ }
}
if (!immediate_request &&
Original Bug Report
Potential Use-After-Free in WidgetBase::UpdateCompositionInfo due to synchronous layout
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-After-Free (UAF) exists in WidgetBase::UpdateCompositionInfo when calculating composition ranges. Forcing a synchronous layout update can trigger JavaScript execution via plugin disposal, allowing an attacker to detach the iframe and destroy the WidgetBase object while it is still actively used on the call stack. This results in a dangling this pointer, which can potentially be exploited for Remote Code Execution in the renderer process.
Affected files:
third_party/blink/renderer/platform/widget/widget_base.ccthird_party/blink/renderer/core/frame/web_frame_widget_impl.ccthird_party/blink/renderer/core/exported/web_input_method_controller_impl.ccthird_party/blink/renderer/core/frame/web_local_frame_impl.cc
Estimated timestamp from git blame: 2020-06-26
Description
A potential Use-After-Free (UAF) vulnerability has been identified in WidgetBase::UpdateCompositionInfo within the Blink renderer. The issue arises because calculating IME composition bounds can force synchronous layout updates, which in turn can execute arbitrary JavaScript. If the JavaScript destroys the iframe containing the WidgetBase, the object is deleted while its this pointer is still actively used on the call stack, leading to a UAF.
Execution Path
The potential vulnerability follows this sequence:
WidgetBase::UpdateCompositionInfois triggered either by a Mojo IPC (RequestCompositionUpdates) or asynchronously by the compositor duringWillBeginMainFrame(viaUpdateSelectionBounds). Crucially, these paths do not employ anImeEventGuardto detect object destruction.- The method calls
GetCompositionRange(&range), which delegates throughWebFrameWidgetImpltoWebInputMethodControllerImpl::CompositionRange. - To accurately determine the composition range of an editable element, the input method controller forces a layout update by calling
editable->GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kInput). - During
UpdateStyleAndLayout, aHTMLFrameOwnerElement::PluginDisposeSuspendScopeis instantiated to defer plugin destruction until layout completes. - If a plugin element (e.g.,
<object>or<embed>) is marked for disposal during this layout phase (e.g., by matching CSS that setsdisplay: none), the~PluginDisposeSuspendScopedestructor will invokeWebPluginContainerImpl::Dispose. WebPluginContainerImpl::Disposeexplicitly permits synchronous JavaScript execution by instantiatingScriptForbiddenScope::AllowUserAgentScriptbefore destroying the plugin.- An attacker’s JavaScript callback executes synchronously and detaches the iframe from the DOM (e.g.,
frameElement.remove()). - Detaching the iframe synchronously invokes
WebFrameWidgetImpl::Close, which callswidget_base_.reset(), instantly freeing the memory backing theWidgetBaseobject. - Execution unwinds back to
WidgetBase::UpdateCompositionInfo. The implicitly usedthispointer is now dangling. - The method proceeds to call
GetCompositionCharacterBounds(&character_bounds). This accesses theclient_member variable from the freed object and attempts a virtual function call (client_->FrameWidget()).
Exploitability
MiraclePtr (BackupRefPtr) does not protect this access. The WidgetBase instance is owned by a std::unique_ptr, meaning its memory is immediately returned to PartitionAlloc. Furthermore, the vulnerability relies on the raw this pointer on the call stack. An attacker can potentially heap-spray the renderer process during the synchronous JavaScript callback to reclaim the freed memory. By overwriting the client_ pointer and controlling its vtable, the attacker could hijack the subsequent virtual function call, achieving Remote Code Execution (RCE) within the sandboxed renderer.
Note: These steps represent a potential attack path derived from static analysis; our tooling agent does not currently have the capability to execute a working Proof of Concept.
Suggested Fix
To remediate this issue, execution paths that trigger UpdateCompositionInfo should be protected against object destruction.
This can be achieved by checking a base::WeakPtr<WidgetBase> before and after any calls that might synchronously trigger layout or script execution (like GetCompositionRange). Alternatively, wrapping the logic inside UpdateCompositionInfo with an ImeEventGuard (if appropriate for the context) would safely detect if the object has been destroyed and safely bail out.
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.