CVE-2026-10957
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ActorUafRegressionBrowserTestchrome/browser/actor/tools/uaf_regression_browsertest.cc |
modified | |
ifchrome/browser/actor/tools/uaf_regression_browsertest.cc |
modified |
Files Changed
chrome/browser/actor/BUILD.gnchrome/browser/actor/tools/select_tool_browsertest.ccchrome/browser/actor/tools/uaf_regression_browsertest.cc
Patch
From 7907fe19b3eda099b6b9a78434c56fd6edb637f5 Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner <[email protected]> Date: Wed, 29 Apr 2026 08:26:00 -0700 Subject: [PATCH] [Glic] Fix UAF and crashes in actor tools during synchronous frame detachment This CL fixes Use-After-Free (UAF) vulnerabilities and potential crashes in several Glic actor tools (Click, Type, and DragAndRelease) that occur when a page script synchronously detaches a frame or modifies the DOM in response to an input event. When these tools dispatch input events (like MouseDown or KeyDown) to the renderer, the page may execute script that results in the destruction of the tool object itself or its associated resources. To handle this safely, we now: - Use base::WeakPtr to check if the tool object is still alive after each synchronous event dispatch. - Return early if the tool has been destroyed, preventing access to freed memory. - Ensure that the WebWidget is re-validated where necessary. Regression tests are added in uaf_regression_browsertest.cc using a nested "ABA" iframe structure to reliably trigger synchronous detachment. This suite covers the new fixes for Click, Type, and DragAndRelease. Bug: 506377279 Change-Id: Ie0753f925b7264a127aa1e37d7e41bcabb25db99 Fixed: 506150628 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7800758 Commit-Queue: Andrew Paseltiner <[email protected]> Reviewed-by: David Bokan <[email protected]> Cr-Commit-Position: refs/heads/main@{#1622471} --- diff --git a/chrome/browser/actor/BUILD.gn b/chrome/browser/actor/BUILD.gn index 9bacadda..4b6e449f5 100644 --- a/chrome/browser/actor/BUILD.gn +++ b/chrome/browser/actor/BUILD.gn @@ -460,6 +460,7 @@ "tools/tab_management_tool_browsertest.cc", "tools/tool_agnostic_browsertest.cc", "tools/type_tool_browsertest.cc", + "tools/uaf_regression_browsertest.cc", "tools/wait_tool_browsertest.cc", "tools/window_management_tool_browsertest.cc", ] diff --git a/chrome/browser/actor/tools/select_tool_browsertest.cc b/chrome/browser/actor/tools/select_tool_browsertest.cc index 4df0768..e52ff28b 100644 --- a/chrome/browser/actor/tools/select_tool_browsertest.cc +++ b/chrome/browser/actor/tools/select_tool_browsertest.cc @@ -408,51 +408,5 @@ "delta"); } -// Regression test for UAF in Glic actor tools. -// See crbug.com/506377279. -IN_PROC_BROWSER_TEST_F(ActorSelectToolBrowserTest, - SelectTool_HandlesSynchronousFrameDetachment) { - GURL outer_url = embedded_https_test_server().GetURL( - "a.test", "/actor/select_tool_uaf_outer.html"); - GURL mid_url = embedded_https_test_server().GetURL( - "b.test", "/actor/select_tool_uaf_mid.html"); - GURL inner_url = embedded_https_test_server().GetURL( - "a.test", "/actor/select_tool_uaf_inner.html"); - - ASSERT_TRUE(content::NavigateToURL(web_contents(), outer_url)); - content::WaitForLoadStop(web_contents()); - - // Set mid iframe src - ASSERT_TRUE(content::ExecJs( - web_contents(), - content::JsReplace("document.getElementById('mid').src = $1", mid_url))); - content::WaitForLoadStop(web_contents()); - - content::RenderFrameHost* mid_rfh = content::ChildFrameAt(main_frame(), 0); - ASSERT_TRUE(mid_rfh); - - // Set inner iframe src - ASSERT_TRUE(content::ExecJs( - mid_rfh, content::JsReplace("document.getElementById('inner').src = $1", - inner_url))); - content::WaitForLoadStop(web_contents()); - - content::RenderFrameHost* inner_rfh = content::ChildFrameAt(mid_rfh, 0); - ASSERT_TRUE(inner_rfh); - - const std::string select_id = "#s"; - const int32_t select_dom_node_id = - GetDOMNodeId(*inner_rfh, select_id).value(); - - // Trigger SelectTool on the inner frame. - std::unique_ptr<ToolRequest> action = - MakeSelectRequest(*inner_rfh, select_dom_node_id, "b"); - ActResultFuture result; - actor_task().Act(ToRequestList(action), result.GetCallback()); - - // With the fix, this should not crash the renderer. - ASSERT_TRUE(result.Wait()); -} - } // namespace } // namespace actor diff --git a/chrome/browser/actor/tools/uaf_regression_browsertest.cc b/chrome/browser/actor/tools/uaf_regression_browsertest.cc new file mode 100644 index 0000000..dd032bba --- /dev/null +++ b/chrome/browser/actor/tools/uaf_regression_browsertest.cc @@ -0,0 +1,153 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/test/test_future.h" +#include "chrome/browser/actor/actor_test_util.h" +#include "chrome/browser/actor/tools/tool_request.h" +#include "chrome/browser/actor/tools/tools_test_util.h" +#include "chrome/common/actor.mojom.h" +#include "chrome/common/actor/action_result.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/point_conversions.h" + +namespace actor { + +namespace { + +using base::test::TestFuture; +using content::ChildFrameAt; +using content::ExecJs; +using content::GetDOMNodeId; +using content::RenderFrameHost; +using ActResultFuture = TestFuture<std::vector<ActionResultWithLatencyInfo>>; + +class ActorUafRegressionBrowserTest : public ActorToolsTest { + public: + void SetUpOnMainThread() override { + ActorToolsTest::SetUpOnMainThread(); + ASSERT_TRUE(embedded_test_server()->Start()); + embedded_https_test_server().SetSSLConfig( + net::EmbeddedTestServer::CERT_TEST_NAMES); + ASSERT_TRUE(embedded_https_test_server().Start()); + } + + void SetupAbaFrames(const std::string& inner_path) { + GURL outer_url = + embedded_https_test_server().GetURL("a.test", "/actor/uaf_outer.html"); + GURL mid_url = + embedded_https_test_server().GetURL("b.test", "/actor/uaf_mid.html"); + GURL inner_url = embedded_https_test_server().GetURL("a.test", inner_path); + + ASSERT_TRUE(content::NavigateToURL(web_contents(), outer_url)); + content::WaitForLoadStop(web_contents()); + + // Set mid iframe src + ASSERT_TRUE( + ExecJs(web_contents(), + content::JsReplace("document.getElementById('mid').src = $1", + mid_url))); + content::WaitForLoadStop(web_contents()); + + RenderFrameHost* mid_rfh = ChildFrameAt(main_frame(), 0); + ASSERT_TRUE(mid_rfh); + + // Set inner iframe src + ASSERT_TRUE(ExecJs( + mid_rfh, content::JsReplace("document.getElementById('inner').src = $1", + inner_url))); + content::WaitForLoadStop(web_contents()); + } + + RenderFrameHost* GetInnerRfh() { + RenderFrameHost* mid_rfh = ChildFrameAt(main_frame(), 0); + if (!mid_rfh) { + return nullptr; + } + return ChildFrameAt(mid_rfh, 0); + } +}; + +// Regression test for UAF in Glic actor tools. +// See crbug.com/506150628. +IN_PROC_BROWSER_TEST_F(ActorUafRegressionBrowserTest, + ClickTool_HandlesSynchronousFrameDetachment) { + SetupAbaFrames("/actor/click_tool_uaf_inner.html"); + RenderFrameHost* inner_rfh = GetInnerRfh(); + ASSERT_TRUE(inner_rfh); + + const int32_t target_id = GetDOMNodeId(*inner_rfh, "#target").value(); + + std::unique_ptr<ToolRequest> action = MakeClickRequest(*inner_rfh, target_id); + ActResultFuture result; + actor_task().Act(ToRequestList(action), result.GetCallback()); + + // This should not crash the renderer. + ASSERT_TRUE(result.Wait()); +} +
Regression Test / PoC
diff --git a/chrome/browser/actor/tools/select_tool_browsertest.cc b/chrome/browser/actor/tools/select_tool_browsertest.cc
index 4df0768..e52ff28b 100644
--- a/chrome/browser/actor/tools/select_tool_browsertest.cc
+++ b/chrome/browser/actor/tools/select_tool_browsertest.cc
@@ -408,51 +408,5 @@
"delta");
}
-// Regression test for UAF in Glic actor tools.
-// See crbug.com/506377279.
-IN_PROC_BROWSER_TEST_F(ActorSelectToolBrowserTest,
- SelectTool_HandlesSynchronousFrameDetachment) {
- GURL outer_url = embedded_https_test_server().GetURL(
- "a.test", "/actor/select_tool_uaf_outer.html");
- GURL mid_url = embedded_https_test_server().GetURL(
- "b.test", "/actor/select_tool_uaf_mid.html");
- GURL inner_url = embedded_https_test_server().GetURL(
- "a.test", "/actor/select_tool_uaf_inner.html");
-
- ASSERT_TRUE(content::NavigateToURL(web_contents(), outer_url));
- content::WaitForLoadStop(web_contents());
-
- // Set mid iframe src
- ASSERT_TRUE(content::ExecJs(
- web_contents(),
- content::JsReplace("document.getElementById('mid').src = $1", mid_url)));
- content::WaitForLoadStop(web_contents());
-
- content::RenderFrameHost* mid_rfh = content::ChildFrameAt(main_frame(), 0);
- ASSERT_TRUE(mid_rfh);
-
- // Set inner iframe src
- ASSERT_TRUE(content::ExecJs(
- mid_rfh, content::JsReplace("document.getElementById('inner').src = $1",
- inner_url)));
- content::WaitForLoadStop(web_contents());
-
- content::RenderFrameHost* inner_rfh = content::ChildFrameAt(mid_rfh, 0);
- ASSERT_TRUE(inner_rfh);
-
- const std::string select_id = "#s";
- const int32_t select_dom_node_id =
- GetDOMNodeId(*inner_rfh, select_id).value();
-
- // Trigger SelectTool on the inner frame.
- std::unique_ptr<ToolRequest> action =
- MakeSelectRequest(*inner_rfh, select_dom_node_id, "b");
- ActResultFuture result;
- actor_task().Act(ToRequestList(action), result.GetCallback());
-
- // With the fix, this should not crash the renderer.
- ASSERT_TRUE(result.Wait());
-}
-
} // namespace
} // namespace actor
diff --git a/chrome/browser/actor/tools/uaf_regression_browsertest.cc b/chrome/browser/actor/tools/uaf_regression_browsertest.cc
new file mode 100644
index 0000000..dd032bba
--- /dev/null
+++ b/chrome/browser/actor/tools/uaf_regression_browsertest.cc
@@ -0,0 +1,153 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_future.h"
+#include "chrome/browser/actor/actor_test_util.h"
+#include "chrome/browser/actor/tools/tool_request.h"
+#include "chrome/browser/actor/tools/tools_test_util.h"
+#include "chrome/common/actor.mojom.h"
+#include "chrome/common/actor/action_result.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_conversions.h"
+
+namespace actor {
+
+namespace {
+
+using base::test::TestFuture;
+using content::ChildFrameAt;
+using content::ExecJs;
+using content::GetDOMNodeId;
+using content::RenderFrameHost;
+using ActResultFuture = TestFuture<std::vector<ActionResultWithLatencyInfo>>;
+
+class ActorUafRegressionBrowserTest : public ActorToolsTest {
+ public:
+ void SetUpOnMainThread() override {
+ ActorToolsTest::SetUpOnMainThread();
+ ASSERT_TRUE(embedded_test_server()->Start());
+ embedded_https_test_server().SetSSLConfig(
+ net::EmbeddedTestServer::CERT_TEST_NAMES);
+ ASSERT_TRUE(embedded_https_test_server().Start());
+ }
+
+ void SetupAbaFrames(const std::string& inner_path) {
+ GURL outer_url =
+ embedded_https_test_server().GetURL("a.test", "/actor/uaf_outer.html");
+ GURL mid_url =
+ embedded_https_test_server().GetURL("b.test", "/actor/uaf_mid.html");
+ GURL inner_url = embedded_https_test_server().GetURL("a.test", inner_path);
+
+ ASSERT_TRUE(content::NavigateToURL(web_contents(), outer_url));
+ content::WaitForLoadStop(web_contents());
+
+ // Set mid iframe src
+ ASSERT_TRUE(
+ ExecJs(web_contents(),
+ content::JsReplace("document.getElementById('mid').src = $1",
+ mid_url)));
+ content::WaitForLoadStop(web_contents());
+
+ RenderFrameHost* mid_rfh = ChildFrameAt(main_frame(), 0);
+ ASSERT_TRUE(mid_rfh);
+
+ // Set inner iframe src
+ ASSERT_TRUE(ExecJs(
+ mid_rfh, content::JsReplace("document.getElementById('inner').src = $1",
+ inner_url)));
+ content::WaitForLoadStop(web_contents());
+ }
+
+ RenderFrameHost* GetInnerRfh() {
+ RenderFrameHost* mid_rfh = ChildFrameAt(main_frame(), 0);
+ if (!mid_rfh) {
+ return nullptr;
+ }
+ return ChildFrameAt(mid_rfh, 0);
+ }
+};
+
+// Regression test for UAF in Glic actor tools.
+// See crbug.com/506150628.
+IN_PROC_BROWSER_TEST_F(ActorUafRegressionBrowserTest,
+ ClickTool_HandlesSynchronousFrameDetachment) {
+ SetupAbaFrames("/actor/click_tool_uaf_inner.html");
+ RenderFrameHost* inner_rfh = GetInnerRfh();
+ ASSERT_TRUE(inner_rfh);
+
+ const int32_t target_id = GetDOMNodeId(*inner_rfh, "#target").value();
+
+ std::unique_ptr<ToolRequest> action = MakeClickRequest(*inner_rfh, target_id);
+ ActResultFuture result;
+ actor_task().Act(ToRequestList(action), result.GetCallback());
+
+ // This should not crash the renderer.
+ ASSERT_TRUE(result.Wait());
+}
+
+// Regression test for UAF in Glic actor tools.
+// See crbug.com/506150628.
+IN_PROC_BROWSER_TEST_F(ActorUafRegressionBrowserTest,
+ TypeTool_HandlesSynchronousFrameDetachment) {
+ SetupAbaFrames("/actor/type_tool_uaf_inner.html");
+ RenderFrameHost* inner_rfh = GetInnerRfh();
+ ASSERT_TRUE(inner_rfh);
+
+ const int32_t target_id = GetDOMNodeId(*inner_rfh, "#target").value();
+
+ std::unique_ptr<ToolRequest> action = MakeTypeRequest(
+ *inner_rfh, target_id, "hello", /* follow_by_enter= */ false);
+ ActResultFuture result;
+ actor_task().Act(ToRequestList(action), result.GetCallback());
+
+ // This should not crash the renderer.
+ ASSERT_TRUE(result.Wait());
+}
+
+// Regression test for UAF in Glic actor tools.
+// See crbug.com/506150628.
+IN_PROC_BROWSER_TEST_F(ActorUafRegressionBrowserTest,
+ DragAndReleaseTool_HandlesSynchronousFrameDetachment) {
+ SetupAbaFrames("/actor/drag_and_release_tool_uaf_inner.html");
+ RenderFrameHost* inner_rfh = GetInnerRfh();
+ ASSERT_TRUE(inner_rfh);
+
+ gfx::RectF bounds = GetBoundingClientRect(*inner_rfh, "#target");
+ gfx::Point from_point = gfx::ToRoundedPoint(bounds.CenterPoint());
+ gfx::Point to_point = from_point + gfx::Vector2d(100, 100);
+
+ // Drag from target to somewhere else.
+ std::unique_ptr<ToolRequest> action =
+ MakeDragAndReleaseRequest(*active_tab(), from_point, to_point);
+ ActResultFuture result;
+ actor_task().Act(ToRequestList(action), result.GetCallback());
+
+ // This should not crash the renderer.
+ ASSERT_TRUE(result.Wait());
+}
+
+// Regression test for UAF in Glic actor tools.
+// See crbug.com/506377279.
+IN_PROC_BROWSER_TEST_F(ActorUafRegressionBrowserTest,
+ SelectTool_HandlesSynchronousFrameDetachment) {
+ SetupAbaFrames("/actor/select_tool_uaf_inner.html");
+ RenderFrameHost* inner_rfh = GetInnerRfh();
+ ASSERT_TRUE(inner_rfh);
+
+ const int32_t target_id = GetDOMNodeId(*inner_rfh, "#s").value();
+
+ std::unique_ptr<ToolRequest> action =
+ MakeSelectRequest(*inner_rfh, target_id, "b");
+ ActResultFuture result;
+ actor_task().Act(ToRequestList(action), result.GetCallback());
+
+ // This should not crash the renderer.
+ ASSERT_TRUE(result.Wait());
+}
+
+} // namespace
+} // namespace actor
diff --git a/chrome/test/data/actor/click_tool_uaf_inner.html b/chrome/test/data/actor/click_tool_uaf_inner.html
new file mode 100644
index 0000000..86ef17c1
--- /dev/null
+++ b/chrome/test/data/actor/click_tool_uaf_inner.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<body>
+ <div id="target" style="width: 100px; height: 100px; background: red;"></div>
+ <script>
+ const target = document.getElementById('target');
+ target.addEventListener('mousedown', () => {
+ // Remove the mid iframe from the top window.
+ // Since mid is cross-origin, removing it synchronously detaches this frame.
+ parent.parent.document.getElementById('mid').remove();
+ });
+ </script>
+</body>
+</html>
diff --git a/chrome/test/data/actor/drag_and_release_tool_uaf_inner.html b/chrome/test/data/actor/drag_and_release_tool_uaf_inner.html
new file mode 100644
index 0000000..19cfb22
--- /dev/null
+++ b/chrome/test/data/actor/drag_and_release_tool_uaf_inner.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<body>
+ <div id="target" style="width: 100px; height: 100px; background: blue;"></div>
+ <script>
+ const target = document.getElementById('target');
+ target.addEventListener('mousedown', () => {
+ parent.parent.document.getElementById('mid').remove();
+ });
+ </script>
+</body>
+</html>
diff --git a/chrome/test/data/actor/select_tool_uaf_mid.html b/chrome/test/data/actor/select_tool_uaf_mid.html
deleted file mode 100644
index 39847961..0000000
--- a/chrome/test/data/actor/select_tool_uaf_mid.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!DOCTYPE html>
-<html>
-<body>
- <iframe id="inner" width="400" height="400"></iframe>
-</body>
-</html>
diff --git a/chrome/test/data/actor/select_tool_uaf_outer.html b/chrome/test/data/actor/select_tool_uaf_outer.html
deleted file mode 100644
index 8a0f98a..0000000
--- a/chrome/test/data/actor/select_tool_uaf_outer.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!DOCTYPE html>
-<html>
-<body>
- <iframe id="mid" width="500" height="500"></iframe>
-</body>
-</html>
diff --git a/chrome/test/data/actor/type_tool_uaf_inner.html b/chrome/test/data/actor/type_tool_uaf_inner.html
new file mode 100644
index 0000000..c55a208b
--- /dev/null
+++ b/chrome/test/data/actor/type_tool_uaf_inner.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<body>
+ <input id="target" type="text">
+ <script>
+ const target = document.getElementById('target');
+ target.addEventListener('keydown', () => {
+ parent.parent.document.getElementById('mid').remove();
+ });
+ </script>
+</body>
+</html>
diff --git a/chrome/test/data/actor/uaf_mid.html b/chrome/test/data/actor/uaf_mid.html
new file mode 100644
index 0000000..39847961
--- /dev/null
+++ b/chrome/test/data/actor/uaf_mid.html
... (truncated)
Original Bug Report
UAF in SelectTool::Execute due to synchronous DOM events
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 Use-After-Free (UAF) vulnerability may exist in the renderer process when the Glic actor’s SelectTool synchronously dispatches DOM events. Page script can destroy the tool’s owning frame, freeing the tool object before execution resumes, leading to a virtual call via an attacker-controlled pointer.
Affected files:
chrome/renderer/actor/select_tool.ccchrome/renderer/actor/tool_base.hchrome/renderer/actor/tool_executor.hchrome/renderer/chrome_render_frame_observer.hcontent/renderer/render_frame_impl.cc
Estimated timestamp from git blame: 2025-12-01
Summary
A potential Use-After-Free (UAF) vulnerability has been identified in actor::SelectTool::Execute within the Chrome renderer process. The vulnerability occurs because setting a <select> element’s value synchronously dispatches DOM events. If page script in an event handler removes the iframe hosting the tool, the entire ownership chain (including the SelectTool instance) is destroyed. Execution then returns to the now-freed SelectTool::Execute, which dereferences this->frame_ leading to a potential Remote Code Execution (RCE) in the renderer.
Root Cause Analysis
In chrome/renderer/actor/select_tool.cc:
void SelectTool::Execute(ToolFinishedCallback callback) {
// ...
WebSelectElement select = validated_target_and_value_.value().select;
WebString value = validated_target_and_value_.value().option_value;
select.SetValue(value, /*send_events=*/true); // [1] Synchronously runs page JS
frame_->GetWebFrame()->View()->CancelPagePopup(); // [2] UAF: reads this->frame_ from freed memory
std::move(callback).Run(MakeOkResult());
}
At [1], select.SetValue with send_events=true eventually calls MenuListSelectType::DidSelectOption. This synchronously dispatches input and change DOM events because there is no active EventQueueScope delaying them in this context.
If the attacker’s page script handles these events and removes the iframe containing the tool (e.g., iframe.remove()), a synchronous teardown sequence is initiated:
RenderFrameImpl::FrameDetachedexecutesdelete this;.- The
RenderFrameImpldestructor destroys its observers, includingChromeRenderFrameObserver. ChromeRenderFrameObserverdeletes itself, destroying itsToolExecutormember.ToolExecutordestroys itsstd::unique_ptr<ToolBase> tool_member, freeing theSelectToolobject.
At [2], the code attempts to access this->frame_. Because this has been freed, and frame_ resides within the freed memory, this is a heap-UAF read.
Exploitation Potential
An attacker has a window during the synchronous change event execution to perform heap spraying. By reclaiming the memory slot previously occupied by SelectTool, the attacker can forge the this->frame_ pointer (which is at a known offset within the object).
BackupRefPtr (MiraclePtr) does not prevent this specific exploit pattern. While BRP protects the target of a raw_ptr/raw_ref, it cannot protect against the modification of the raw_ref object itself when its containing object (SelectTool) is freed and overwritten. In release builds, operator-> on the BRP raw_ref simply returns the forged pointer.
The subsequent call to GetWebFrame() is a pure virtual method. By providing a fake vtable via the forged pointer, the attacker gains a strong primitive for arbitrary code execution within the sandboxed renderer.
Note: These steps are theoretical, as our tooling agent does not have the ability to run code to confirm a full exploit.
Reproduction Steps (Suggested)
- Create a site-isolated setup (e.g., A → B → A iframes) where the inner frame A can remove the intermediate frame B without terminating its own V8 context.
- In the inner frame A, place a
<select>element and attach achangeevent listener. - The
changehandler removes the intermediate frame B (triggering tool destruction) and performs heap spraying to overwrite theSelectToolmemory slot with a forgedframe_pointer. - Trigger the Glic agent to perform a
SelectActionon the<select>element. - The renderer should attempt to dereference the attacker-controlled pointer at
select_tool.cc:57.
Suggested Fix
There are two primary ways to fix this issue:
- Reorder operations: Call
frame_->GetWebFrame()->View()->CancelPagePopup();beforeselect.SetValue(). This removes the immediate risk on line 57, though the subsequent callback execution must also be carefully reviewed for UAF risks. - Weak Pointers: Introduce a
base::WeakPtrFactory<SelectTool>and take a weak pointer before callingSetValue. AfterSetValuereturns, check if the weak pointer is still valid before proceeding.
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.
Raised in root component due to access or custom field issues on 1707859