High chrome Memory Corruption 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMemory corruption in HTML
DescriptionMemory corruption in HTML
ComponentHTML
Sub-componentCE
Bug ClassMemory Corruption
Tracker568730797
Fix commit6563f89211c4 (chromium/src) +75/-4
CISA KEVNot listed
CreditedXinyang Ge
Disclosed2026-10-27

Changed Functions

FunctionChangeNotes
if
third_party/blink/web_tests/external/wpt/html/semantics/permission-element/geolocation-element/remove-iframe-during-register-crash.html
modified
next
third_party/blink/web_tests/external/wpt/html/semantics/permission-element/geolocation-element/remove-iframe-during-register-crash.html
modified

Files Changed

  • third_party/blink/renderer/core/html/html_capability_element_base.cc
  • third_party/blink/web_tests/external/wpt/html/semantics/permission-element/geolocation-element/remove-iframe-during-register-crash.html
From 6563f89211c484d3cfb7866d875045d806e505a4 Mon Sep 17 00:00:00 2001
From: Ravjit Uppal <ravjit@chromium.org>
Date: Thu, 15 Oct 2026 09:12:06 -0700
Subject: [PATCH] [CE]Prevent crash when frame is detached during capability element registration

Creating the embedded permission request descriptor can trigger layout
updates or script execution that detaches the frame or execution
context.

This change invokes CreateEmbeddedPermissionRequestDescriptor earlier
and verifies that the frame, rendered state, receiver binding, and
execution context remain valid before proceeding with registration or
permission requests.

Bug: 568730797
Change-Id: Icd2ac6cfc17a222620e31cbbab88cc31d7c86b7b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8572457
Reviewed-by: Joey Arhar <jarhar@chromium.org>
Reviewed-by: Thomas Nguyen <tungnh@chromium.org>
Auto-Submit: Ravjit Uppal <ravjit@chromium.org>
Commit-Queue: Ravjit Uppal <ravjit@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1719744}
---
diff --git a/third_party/blink/renderer/core/html/html_capability_element_base.cc b/third_party/blink/renderer/core/html/html_capability_element_base.cc
index ad94256..c7e1bbf 100644
--- a/third_party/blink/renderer/core/html/html_capability_element_base.cc
+++ b/third_party/blink/renderer/core/html/html_capability_element_base.cc
@@ -693,13 +693,25 @@
     return false;
   }
 
+  // `CreateEmbeddedPermissionRequestDescriptor()` computes `BoundsInWidget()`,
+  // which forces a synchronous style and layout update. That update can
+  // re-entrantly register this control, detach the frame, or change whether
+  // the element is rendered, so re-verify these conditions afterwards.
+  auto descriptor = CreateEmbeddedPermissionRequestDescriptor();
+  if (embedded_permission_control_receiver_.is_bound()) {
+    return true;
+  }
+  if (!GetDocument().GetFrame() || !IsRendered()) {
+    return false;
+  }
+
   mojo::PendingRemote<EmbeddedPermissionControlClient> client;
   embedded_permission_control_receiver_.Bind(
       client.InitWithNewPipeAndPassReceiver(), GetTaskRunner());
   CHECK(embedded_permission_control_receiver_.is_bound());
   GetPermissionService()->RegisterPageEmbeddedPermissionControl(
-      mojo::Clone(permission_descriptors_),
-      CreateEmbeddedPermissionRequestDescriptor(), std::move(client));
+      mojo::Clone(permission_descriptors_), std::move(descriptor),
+      std::move(client));
   return true;
 }
 
@@ -1133,9 +1145,15 @@
   CHECK_LE(permission_descriptors_.size(), 2U);
   pending_request_created_ = base::TimeTicks::Now();
 
+  // `CreateEmbeddedPermissionRequestDescriptor()` forces a synchronous layout
+  // update, which may detach the frame or make the element unrendered.
+  auto descriptor = CreateEmbeddedPermissionRequestDescriptor();
+  if (!GetDocument().GetFrame() || !IsRendered()) {
+    return;
+  }
+
   GetPermissionService()->RequestPageEmbeddedPermission(
-      mojo::Clone(permission_descriptors_),
-      CreateEmbeddedPermissionRequestDescriptor(),
+      mojo::Clone(permission_descriptors_), std::move(descriptor),
       BindOnce(&HTMLCapabilityElementBase::OnEmbeddedPermissionsDecided,
                WrapWeakPersistent(this)));
 }
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/geolocation-element/remove-iframe-during-register-crash.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/geolocation-element/remove-iframe-during-register-crash.html
new file mode 100644
index 0000000..42f16249
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/geolocation-element/remove-iframe-during-register-crash.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Detaching iframe during geolocation registration layout should not crash</title>
+<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md">
+<body>
+<svg width="0" height="0"><defs>
+<g id="a"><rect id="r" width="10" height="10" tabindex="0"/></g>
+<g id="b"><rect width="10" height="10" tabindex="0"/></g>
+</defs></svg>
+<dialog id="d"><svg width="20" height="20"><use id="v" href="#a"/></svg></dialog>
+<script>
+let useInstance = null;
+r.addEventListener('focus', (e) => {
+  useInstance = e.currentTarget;
+}, { once: true });
+
+d.show();
+
+const inner = document.createElement('iframe');
+if (useInstance) {
+  const fo = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
+  fo.appendChild(inner);
+  useInstance.parentNode.appendChild(fo);
+} else {
+  document.body.appendChild(inner);
+}
+
+let inside = false;
+const iterator = {
+  next() { return new Promise(() => {}); },
+  get return() {
+    if (inside) {
+      inner.remove();
+    }
+  }
+};
+if (typeof Observable !== 'undefined' && document.when) {
+  Observable.from({ [Symbol.asyncIterator]() { return iterator; } })
+      .takeUntil(document.when('focusout'))
+      .subscribe({});
+}
+
+const doc = inner.contentDocument;
+const control = doc.createElement('geolocation');
+doc.body.appendChild(control);
+inner.contentWindow.getComputedStyle(control).color;
+
+v.setAttribute('href', '#b');
+inside = true;
+control.setAttribute('data-x', '1');
+inside = false;
+</script>
+</body>
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/geolocation-element/remove-iframe-during-register-crash.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/geolocation-element/remove-iframe-during-register-crash.html
new file mode 100644
index 0000000..42f16249
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/geolocation-element/remove-iframe-during-register-crash.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Detaching iframe during geolocation registration layout should not crash</title>
+<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md">
+<body>
+<svg width="0" height="0"><defs>
+<g id="a"><rect id="r" width="10" height="10" tabindex="0"/></g>
+<g id="b"><rect width="10" height="10" tabindex="0"/></g>
+</defs></svg>
+<dialog id="d"><svg width="20" height="20"><use id="v" href="#a"/></svg></dialog>
+<script>
+let useInstance = null;
+r.addEventListener('focus', (e) => {
+  useInstance = e.currentTarget;
+}, { once: true });
+
+d.show();
+
+const inner = document.createElement('iframe');
+if (useInstance) {
+  const fo = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
+  fo.appendChild(inner);
+  useInstance.parentNode.appendChild(fo);
+} else {
+  document.body.appendChild(inner);
+}
+
+let inside = false;
+const iterator = {
+  next() { return new Promise(() => {}); },
+  get return() {
+    if (inside) {
+      inner.remove();
+    }
+  }
+};
+if (typeof Observable !== 'undefined' && document.when) {
+  Observable.from({ [Symbol.asyncIterator]() { return iterator; } })
+      .takeUntil(document.when('focusout'))
+      .subscribe({});
+}
+
+const doc = inner.contentDocument;
+const control = doc.createElement('geolocation');
+doc.body.appendChild(control);
+inner.contentWindow.getComputedStyle(control).color;
+
+v.setAttribute('href', '#b');
+inside = true;
+control.setAttribute('data-x', '1');
+inside = false;
+</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.