Chrome · Frame
CVE-2026-17971
Logic Error in Frame
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/blink/web_tests/printing/remove-iframe-beforeprint-crash.html |
modified |
Files Changed
third_party/blink/renderer/core/frame/local_frame.ccthird_party/blink/web_tests/printing/remove-iframe-beforeprint-crash.htmlthird_party/blink/web_tests/printing/resources/remove-iframe-beforeprint-crash-child.html
Patch
From b93fe74ce13916f57acd73148d2ec8e5aa7b7ed0 Mon Sep 17 00:00:00 2001
From: Mason Freed <masonf@chromium.org>
Date: Tue, 09 Jun 2026 09:37:38 -0700
Subject: [PATCH] Null-check GetFrameScheduler() in LocalFrame::SetHadUserInteraction()
Calling SetHadUserInteraction() on a frame that gets detached during
unwinding or inside synchronous print events can encounter a null
FrameScheduler. This adds a check for GetFrameScheduler() before
calling SetHadUserActivation().
Fixed: 518815075
Change-Id: I783b20ebcb682c648fd907d170895b787dbe40d7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7905327
Reviewed-by: Joey Arhar <jarhar@chromium.org>
Commit-Queue: Mason Freed <masonf@chromium.org>
Auto-Submit: Mason Freed <masonf@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1644040}
---
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 20ff202..7b016194 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -2927,7 +2927,9 @@
DomWindow()->closewatcher_stack()->SetHadUserInteraction(
had_user_interaction);
- GetFrameScheduler()->SetHadUserActivation(had_user_interaction);
+ if (auto* scheduler = GetFrameScheduler()) {
+ scheduler->SetHadUserActivation(had_user_interaction);
+ }
}
namespace {
diff --git a/third_party/blink/web_tests/printing/remove-iframe-beforeprint-crash.html b/third_party/blink/web_tests/printing/remove-iframe-beforeprint-crash.html
new file mode 100644
index 0000000..3a612ae
--- /dev/null
+++ b/third_party/blink/web_tests/printing/remove-iframe-beforeprint-crash.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://crbug.com/518815075">
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="../resources/testdriver.js"></script>
+<script src="../resources/testdriver-actions.js"></script>
+<script src="../resources/testdriver-vendor.js"></script>
+
+<!-- This test passes if it doesn't crash. -->
+
+<iframe src="resources/remove-iframe-beforeprint-crash-child.html"></iframe>
+
+<script>
+async_test(t => {
+ window.finishTest = t.step_func_done(() => {});
+
+ window.addEventListener('beforeprint', t.step_func(() => {
+ let f = document.querySelector('iframe');
+ if (f) {
+ f.remove();
+ }
+ }), { once: true });
+}, "This is a crash test and it passes if it doesn't crash.");
+</script>
+
diff --git a/third_party/blink/web_tests/printing/resources/remove-iframe-beforeprint-crash-child.html b/third_party/blink/web_tests/printing/resources/remove-iframe-beforeprint-crash-child.html
new file mode 100644
index 0000000..f682ec1
--- /dev/null
+++ b/third_party/blink/web_tests/printing/resources/remove-iframe-beforeprint-crash-child.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://crbug.com/518815075">
+
+<body style="margin:0">
+
+<div id="clicktarget">CLICK HERE</div>
+
+<form id="login" action="/login" method="post"
+ style="position:absolute;left:0;top:0;opacity:0.01;pointer-events:none">
+ <input type="text" id="username" name="username" autocomplete="username">
+ <input type="password" id="password" name="password" autocomplete="current-password">
+ <input type="submit" value="go">
+</form>
+
+<script>
+let armed = false;
+
+function trigger() {
+ if (!armed) return;
+ armed = false;
+ top.print();
+ top.finishTest();
+}
+
+username.addEventListener('input', trigger);
+username.addEventListener('change', trigger);
+password.addEventListener('input', trigger);
+clicktarget.addEventListener('mousedown', trigger);
+
+window.addEventListener('load', () => {
+ setTimeout(() => {
+ armed = true;
+ new top.test_driver.Actions()
+ .pointerMove(0, 0, {origin: clicktarget})
+ .pointerDown()
+ .pointerUp()
+ .send();
+ }, 0);
+});
+</script>
+</body>
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/third_party/blink/web_tests/printing/remove-iframe-beforeprint-crash.html b/third_party/blink/web_tests/printing/remove-iframe-beforeprint-crash.html
new file mode 100644
index 0000000..3a612ae
--- /dev/null
+++ b/third_party/blink/web_tests/printing/remove-iframe-beforeprint-crash.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://crbug.com/518815075">
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="../resources/testdriver.js"></script>
+<script src="../resources/testdriver-actions.js"></script>
+<script src="../resources/testdriver-vendor.js"></script>
+
+<!-- This test passes if it doesn't crash. -->
+
+<iframe src="resources/remove-iframe-beforeprint-crash-child.html"></iframe>
+
+<script>
+async_test(t => {
+ window.finishTest = t.step_func_done(() => {});
+
+ window.addEventListener('beforeprint', t.step_func(() => {
+ let f = document.querySelector('iframe');
+ if (f) {
+ f.remove();
+ }
+ }), { once: true });
+}, "This is a crash test and it passes if it doesn't crash.");
+</script>
+
diff --git a/third_party/blink/web_tests/printing/resources/remove-iframe-beforeprint-crash-child.html b/third_party/blink/web_tests/printing/resources/remove-iframe-beforeprint-crash-child.html
new file mode 100644
index 0000000..f682ec1
--- /dev/null
+++ b/third_party/blink/web_tests/printing/resources/remove-iframe-beforeprint-crash-child.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://crbug.com/518815075">
+
+<body style="margin:0">
+
+<div id="clicktarget">CLICK HERE</div>
+
+<form id="login" action="/login" method="post"
+ style="position:absolute;left:0;top:0;opacity:0.01;pointer-events:none">
+ <input type="text" id="username" name="username" autocomplete="username">
+ <input type="password" id="password" name="password" autocomplete="current-password">
+ <input type="submit" value="go">
+</form>
+
+<script>
+let armed = false;
+
+function trigger() {
+ if (!armed) return;
+ armed = false;
+ top.print();
+ top.finishTest();
+}
+
+username.addEventListener('input', trigger);
+username.addEventListener('change', trigger);
+password.addEventListener('input', trigger);
+clicktarget.addEventListener('mousedown', trigger);
+
+window.addEventListener('load', () => {
+ setTimeout(() => {
+ armed = true;
+ new top.test_driver.Actions()
+ .pointerMove(0, 0, {origin: clicktarget})
+ .pointerDown()
+ .pointerUp()
+ .send();
+ }, 0);
+});
+</script>
+</body>
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page