Chrome · PermissionElement
CVE-2026-79098
Logic Error in PermissionElement
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/blink/renderer/core/html/html_capability_element_base.cc |
modified | |
TEST_Fthird_party/blink/renderer/core/html/html_capability_element_base_test.cc |
modified |
Files Changed
third_party/blink/renderer/core/html/html_capability_element_base.ccthird_party/blink/renderer/core/html/html_capability_element_base.hthird_party/blink/renderer/core/html/html_capability_element_base_test.cc
Patch
From 3a9b444e1f27bbf25c28289f93de51e443678f5a Mon Sep 17 00:00:00 2001
From: Stefan Zager <szager@chromium.org>
Date: Fri, 10 Jul 2026 12:49:50 -0700
Subject: [PATCH] [IOv2] Delete visibility observer when pepc element removed from DOM
Bug: 514010111
Change-Id: I6b5f58b149505ed315bb91bbd5c4de9110967971
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8067916
Reviewed-by: Philip Rogers <pdr@chromium.org>
Commit-Queue: Stefan Zager <szager@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1660470}
---
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 ba61a89..69460a67c 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
@@ -360,6 +360,10 @@
if (auto* view = GetDocument().View()) {
view->UnregisterFromLifecycleNotifications(this);
}
+ if (!performing_reattach && intersection_observer_) {
+ intersection_observer_->disconnect();
+ intersection_observer_ = nullptr;
+ }
}
void HTMLCapabilityElementBase::RemovedFrom(ContainerNode& insertion_point) {
diff --git a/third_party/blink/renderer/core/html/html_capability_element_base.h b/third_party/blink/renderer/core/html/html_capability_element_base.h
index 5e01e30..0c71c7f 100644
--- a/third_party/blink/renderer/core/html/html_capability_element_base.h
+++ b/third_party/blink/renderer/core/html/html_capability_element_base.h
@@ -211,6 +211,8 @@
FRIEND_TEST_ALL_PREFIXES(HTMLCapabilityElementBaseIntersectionTest,
IntersectionChanged);
FRIEND_TEST_ALL_PREFIXES(HTMLCapabilityElementBaseIntersectionTest,
+ MovePEPCFromIframeAndDestroyIframe);
+ FRIEND_TEST_ALL_PREFIXES(HTMLCapabilityElementBaseIntersectionTest,
IntersectionChangedDisableEnableDisable);
FRIEND_TEST_ALL_PREFIXES(HTMLCapabilityElementBaseIntersectionTest,
ContainerDivRotates);
diff --git a/third_party/blink/renderer/core/html/html_capability_element_base_test.cc b/third_party/blink/renderer/core/html/html_capability_element_base_test.cc
index 2886eaf8..bcaca0a6 100644
--- a/third_party/blink/renderer/core/html/html_capability_element_base_test.cc
+++ b/third_party/blink/renderer/core/html/html_capability_element_base_test.cc
@@ -296,7 +296,6 @@
}
GetDocument().body()->AppendChild(permission_element);
- GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
GetDocument().View()->UpdateAllLifecyclePhasesForTest();
return permission_element;
}
@@ -1637,6 +1636,61 @@
}
};
+TEST_F(HTMLCapabilityElementBaseIntersectionTest,
+ MovePEPCFromIframeAndDestroyIframe) {
+ GetDocument().GetSettings()->SetDefaultFontSize(12);
+ SimRequest main_resource("https://example.test/", "text/html");
+ SimRequest iframe_resource("https://example.test/foo.html", "text/html");
+ LoadURL("https://example.test/");
+ main_resource.Complete(R"HTML(
+ <body>
+ <style>
+ #occluder {
+ position:absolute;
+ left:0;
+ top:0;
+ width:100px;
+ height:100px;
+ background:black;
+ }
+ #iframe {
+ position:absolute;
+ left:0;
+ top:150px;
+ }
+ </style>
+ <div id="occluder"></div>
+ <iframe id="iframe" src='https://example.test/foo.html' allow="camera *">
+ </iframe>
+ </body>
+ )HTML");
+ iframe_resource.Complete(R"HTML(
+ <!DOCTYPE html><usermedia id='camera' type='camera'></usermedia>
+ )HTML");
+
+ Compositor().BeginFrame();
+
+ auto* subframe = To<WebLocalFrameImpl>(MainFrame().FirstChild())->GetFrame();
+ auto* permission_element = static_cast<HTMLCapabilityElementBase*>(
+ subframe->GetDocument()->getElementById(AtomicString("camera")));
+ DeferredChecker checker1(permission_element);
+ checker1.CheckClickingEnabledAfterDelay(kDefaultTimeout,
+ /*expected_enabled*/ true);
+
+ // Move PEPC to main document and remove iframe
+ auto& main_document = *MainFrame().GetFrame()->GetDocument();
+ main_document.body()->AppendChild(permission_element);
+ main_document.getElementById(AtomicString("iframe"))->remove();
+
+ Compositor().BeginFrame();
+
+ // Verify it is occluded and clicking is disabled.
+ WaitForIntersectionVisibilityChanged(
+ permission_element,
+ HTMLCapabilityElementBase::IntersectionVisibility::kOccludedOrDistorted);
+ EXPECT_FALSE(permission_element->IsClickingEnabled());
+}
+
TEST_F(HTMLCapabilityElementBaseIntersectionTest, IntersectionChanged) {
GetDocument().GetSettings()->SetDefaultFontSize(12);
SimRequest main_resource("https://example.test/", "text/html");
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/third_party/blink/renderer/core/html/html_capability_element_base_test.cc b/third_party/blink/renderer/core/html/html_capability_element_base_test.cc
index 2886eaf8..bcaca0a6 100644
--- a/third_party/blink/renderer/core/html/html_capability_element_base_test.cc
+++ b/third_party/blink/renderer/core/html/html_capability_element_base_test.cc
@@ -296,7 +296,6 @@
}
GetDocument().body()->AppendChild(permission_element);
- GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
GetDocument().View()->UpdateAllLifecyclePhasesForTest();
return permission_element;
}
@@ -1637,6 +1636,61 @@
}
};
+TEST_F(HTMLCapabilityElementBaseIntersectionTest,
+ MovePEPCFromIframeAndDestroyIframe) {
+ GetDocument().GetSettings()->SetDefaultFontSize(12);
+ SimRequest main_resource("https://example.test/", "text/html");
+ SimRequest iframe_resource("https://example.test/foo.html", "text/html");
+ LoadURL("https://example.test/");
+ main_resource.Complete(R"HTML(
+ <body>
+ <style>
+ #occluder {
+ position:absolute;
+ left:0;
+ top:0;
+ width:100px;
+ height:100px;
+ background:black;
+ }
+ #iframe {
+ position:absolute;
+ left:0;
+ top:150px;
+ }
+ </style>
+ <div id="occluder"></div>
+ <iframe id="iframe" src='https://example.test/foo.html' allow="camera *">
+ </iframe>
+ </body>
+ )HTML");
+ iframe_resource.Complete(R"HTML(
+ <!DOCTYPE html><usermedia id='camera' type='camera'></usermedia>
+ )HTML");
+
+ Compositor().BeginFrame();
+
+ auto* subframe = To<WebLocalFrameImpl>(MainFrame().FirstChild())->GetFrame();
+ auto* permission_element = static_cast<HTMLCapabilityElementBase*>(
+ subframe->GetDocument()->getElementById(AtomicString("camera")));
+ DeferredChecker checker1(permission_element);
+ checker1.CheckClickingEnabledAfterDelay(kDefaultTimeout,
+ /*expected_enabled*/ true);
+
+ // Move PEPC to main document and remove iframe
+ auto& main_document = *MainFrame().GetFrame()->GetDocument();
+ main_document.body()->AppendChild(permission_element);
+ main_document.getElementById(AtomicString("iframe"))->remove();
+
+ Compositor().BeginFrame();
+
+ // Verify it is occluded and clicking is disabled.
+ WaitForIntersectionVisibilityChanged(
+ permission_element,
+ HTMLCapabilityElementBase::IntersectionVisibility::kOccludedOrDistorted);
+ EXPECT_FALSE(permission_element->IsClickingEnabled());
+}
+
TEST_F(HTMLCapabilityElementBaseIntersectionTest, IntersectionChanged) {
GetDocument().GetSettings()->SetDefaultFontSize(12);
SimRequest main_resource("https://example.test/", "text/html");
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