Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMissing authorization in Core
DescriptionMissing authorization in Core
ComponentCore
Bug ClassLogic Error
Tracker502452118
Fix commit0488f340b540 (chromium/src) +88/-14
CISA KEVNot listed
CreditedGoogle
Disclosed2026-09-08

Files Changed

  • content/browser/bad_message.h
  • content/browser/browser_interface_binders.cc
  • content/browser/security_exploit_browsertest.cc
  • third_party/blink/renderer/modules/vibration/vibration_controller.cc
From 0488f340b54088e3ecf978b6a2ff7227ccb187de Mon Sep 17 00:00:00 2001
From: Shunya Shishido <[email protected]>
Date: Thu, 30 Jul 2026 22:58:59 -0700
Subject: [PATCH] Reject VibrationManager bind requests from fenced frames

The renderer already refuses navigator.vibrate() inside a fenced-frame
tree, but the browser-side device::mojom::VibrationManager binder did
not enforce the same restriction. Mirror the adjacent
BindBatteryMonitor() pattern: factor the lambda into
BindVibrationManager(), check IsNestedWithinFencedFrame(), and terminate
the renderer with the new BIBI_BIND_VIBRATION_MANAGER_FOR_FENCED_FRAME
reason instead of binding the receiver to the device service.

In addition, update VibrationController::vibrate() in Blink to check
IsInFencedFrameTree() before calling From(navigator). This prevents
VibrationController instantiation and unconditional Mojo interface
binding in fenced frames, avoiding unexpected renderer terminations
during web platform tests (navigator-vibrate.https.html).

Add a content_browsertest that requests the interface directly on a
fenced frame's BrowserInterfaceBroker and asserts the renderer is
terminated with the new bad-message reason.

TAG=agy
CONV=0f7e37f2-2d45-401e-9bf7-0a3ac2b8fc8e

Bug: 502452118
Change-Id: Ic81c2d205150577c13c50f8bcd4d4328145012a1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8173483
Reviewed-by: Rakina Zata Amni <[email protected]>
Reviewed-by: Alexei Svitkine <[email protected]>
Reviewed-by: Takashi Toyoshima <[email protected]>
Commit-Queue: Shunya Shishido <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1671637}
---

diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h
index 8fa45ea3..baddc1a5 100644
--- a/content/browser/bad_message.h
+++ b/content/browser/bad_message.h
@@ -394,6 +394,7 @@
   RFH_INVALID_CONNECTION_ALLOWLIST_ATTRIBUTE = 366,
   PMM_UNSUBSCRIBE_IN_FENCED_FRAME = 367,
   PMM_GET_SUBSCRIPTION_IN_FENCED_FRAME = 368,
+  BIBI_BIND_VIBRATION_MANAGER_FOR_FENCED_FRAME = 369,
 
   // Please add new elements here. The naming convention is abbreviated class
   // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index 94c73d9..6f080e0 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -741,6 +741,20 @@
   GetDeviceService().BindBatteryMonitor(std::move(receiver));
 }
 
+void BindVibrationManager(
+    RenderFrameHost* host,
+    mojo::PendingReceiver<device::mojom::VibrationManager> receiver) {
+  if (host->IsNestedWithinFencedFrame()) {
+    bad_message::ReceivedBadMessage(
+        host->GetProcess(), bad_message::BadMessageReason::
+                                BIBI_BIND_VIBRATION_MANAGER_FOR_FENCED_FRAME);
+    return;
+  }
+  GetDeviceService().BindVibrationManager(
+      std::move(receiver), static_cast<RenderFrameHostImpl*>(host)
+                               ->CreateVibrationManagerListener());
+}
+
 #if BUILDFLAG(ENABLE_COMPUTE_PRESSURE)
 void BindPressureManager(
     RenderFrameHost* host,
@@ -1434,13 +1448,7 @@
   map->Add<blink::mojom::AnchorElementInteractionHost>(
       &AnchorElementInteractionHostImpl::Create);
 
-  map->Add<device::mojom::VibrationManager>(
-      [](RenderFrameHost* host,
-         mojo::PendingReceiver<device::mojom::VibrationManager> receiver) {
-        GetDeviceService().BindVibrationManager(
-            std::move(receiver), static_cast<RenderFrameHostImpl*>(host)
-                                     ->CreateVibrationManagerListener());
-      });
+  map->Add<device::mojom::VibrationManager>(&BindVibrationManager);
 
 #if BUILDFLAG(IS_CHROMEOS)
   if (base::FeatureList::IsEnabled(features::kWebLockScreenApi)) {
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index 2a3793a..c1d86a44 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -102,7 +102,9 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/device/public/mojom/battery_monitor.mojom.h"
 #include "services/device/public/mojom/screen_orientation.mojom.h"
+#include "services/device/public/mojom/vibration_manager.mojom.h"
 #include "services/network/public/cpp/network_switches.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/resource_request_body.h"
@@ -4175,6 +4177,66 @@
       << handle->GetInitiatorOrigin()->Serialize();
 }
 
+// Verify that binding VibrationManager is rejected when requested from within a
+// fenced frame.
+IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTestFencedFrames,
+                       VibrationManagerBindRejectedInFencedFrame) {
+  GURL main_url(https_server()->GetURL("a.test", "/simple_page.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  GURL fenced_url(
+      https_server()->GetURL("a.test", "/fenced_frames/title1.html"));
+  RenderFrameHost* fenced_rfh_generic =
+      fenced_frame_test_helper().CreateFencedFrame(
+          shell()->web_contents()->GetPrimaryMainFrame(), fenced_url);
+  ASSERT_TRUE(fenced_rfh_generic);
+  RenderFrameHostImpl* fenced_rfh =
+      static_cast<RenderFrameHostImpl*>(fenced_rfh_generic);
+
+  RenderProcessHostBadIpcMessageWaiter kill_waiter(fenced_rfh->GetProcess());
+
+  mojo::Receiver<blink::mojom::BrowserInterfaceBroker>& broker_receiver =
+      fenced_rfh->browser_interface_broker_receiver_for_testing();
+  blink::mojom::BrowserInterfaceBroker* broker =
+      broker_receiver.internal_state()->impl();
+
+  mojo::Remote<device::mojom::VibrationManager> remote;
+  broker->GetInterface(remote.BindNewPipeAndPassReceiver());
+
+  EXPECT_EQ(bad_message::BIBI_BIND_VIBRATION_MANAGER_FOR_FENCED_FRAME,
+            kill_waiter.Wait());
+}
+
+// Verify that binding BatteryMonitor is rejected when requested from within a
+// fenced frame.
+IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTestFencedFrames,
+                       BatteryMonitorBindRejectedInFencedFrame) {
+  GURL main_url(https_server()->GetURL("a.test", "/simple_page.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  GURL fenced_url(
+      https_server()->GetURL("a.test", "/fenced_frames/title1.html"));
+  RenderFrameHost* fenced_rfh_generic =
+      fenced_frame_test_helper().CreateFencedFrame(
+          shell()->web_contents()->GetPrimaryMainFrame(), fenced_url);
+  ASSERT_TRUE(fenced_rfh_generic);
+  RenderFrameHostImpl* fenced_rfh =
+      static_cast<RenderFrameHostImpl*>(fenced_rfh_generic);
+
+  RenderProcessHostBadIpcMessageWaiter kill_waiter(fenced_rfh->GetProcess());
+
+  mojo::Receiver<blink::mojom::BrowserInterfaceBroker>& broker_receiver =
+      fenced_rfh->browser_interface_broker_receiver_for_testing();
+  blink::mojom::BrowserInterfaceBroker* broker =
+      broker_receiver.internal_state()->impl();
+
+  mojo::Remote<device::mojom::BatteryMonitor> remote;
+  broker->GetInterface(remote.BindNewPipeAndPassReceiver());
+
+  EXPECT_EQ(bad_message::BIBI_BIND_BATTERY_MONITOR_FOR_FENCED_FRAME,
+            kill_waiter.Wait());
+}
+
 namespace {
 
 // Interceptor that replaces the origin in the DidCommitProvisionalLoadParams
diff --git a/third_party/blink/renderer/modules/vibration/vibration_controller.cc b/third_party/blink/renderer/modules/vibration/vibration_controller.cc
index 9950d048..3ba3da13 100644
--- a/third_party/blink/renderer/modules/vibration/vibration_controller.cc
+++ b/third_party/blink/renderer/modules/vibration/vibration_controller.cc
@@ -108,6 +108,15 @@
   // reference to |window| or |navigator| was retained in another window.
   if (!navigator.DomWindow())
     return false;
+
+  LocalFrame* frame = navigator.DomWindow()->GetFrame();
+  if (frame && frame->IsInFencedFrameTree()) {
+    Intervention::GenerateReport(
+        frame, "NavigatorVibrate",
+        "Blocked call to navigator.vibrate inside a fenced frame.");
+    return false;
+  }
+
   return From(navigator).Vibrate(pattern);
 }
 
@@ -133,13 +142,6 @@
   UseCounter::Count(DomWindow(), WebFeature::kNavigatorVibrate);
 
   LocalFrame* frame = DomWindow()->GetFrame();
-  if (frame->IsInFencedFrameTree()) {
-    Intervention::GenerateReport(
-        frame, "NavigatorVibrate",
-        "Blocked call to navigator.vibrate inside a fenced frame.");
-    return false;
-  }
-
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index 2a3793a..c1d86a44 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -102,7 +102,9 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/device/public/mojom/battery_monitor.mojom.h"
 #include "services/device/public/mojom/screen_orientation.mojom.h"
+#include "services/device/public/mojom/vibration_manager.mojom.h"
 #include "services/network/public/cpp/network_switches.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/resource_request_body.h"
@@ -4175,6 +4177,66 @@
       << handle->GetInitiatorOrigin()->Serialize();
 }
 
+// Verify that binding VibrationManager is rejected when requested from within a
+// fenced frame.
+IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTestFencedFrames,
+                       VibrationManagerBindRejectedInFencedFrame) {
+  GURL main_url(https_server()->GetURL("a.test", "/simple_page.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  GURL fenced_url(
+      https_server()->GetURL("a.test", "/fenced_frames/title1.html"));
+  RenderFrameHost* fenced_rfh_generic =
+      fenced_frame_test_helper().CreateFencedFrame(
+          shell()->web_contents()->GetPrimaryMainFrame(), fenced_url);
+  ASSERT_TRUE(fenced_rfh_generic);
+  RenderFrameHostImpl* fenced_rfh =
+      static_cast<RenderFrameHostImpl*>(fenced_rfh_generic);
+
+  RenderProcessHostBadIpcMessageWaiter kill_waiter(fenced_rfh->GetProcess());
+
+  mojo::Receiver<blink::mojom::BrowserInterfaceBroker>& broker_receiver =
+      fenced_rfh->browser_interface_broker_receiver_for_testing();
+  blink::mojom::BrowserInterfaceBroker* broker =
+      broker_receiver.internal_state()->impl();
+
+  mojo::Remote<device::mojom::VibrationManager> remote;
+  broker->GetInterface(remote.BindNewPipeAndPassReceiver());
+
+  EXPECT_EQ(bad_message::BIBI_BIND_VIBRATION_MANAGER_FOR_FENCED_FRAME,
+            kill_waiter.Wait());
+}
+
+// Verify that binding BatteryMonitor is rejected when requested from within a
+// fenced frame.
+IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTestFencedFrames,
+                       BatteryMonitorBindRejectedInFencedFrame) {
+  GURL main_url(https_server()->GetURL("a.test", "/simple_page.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  GURL fenced_url(
+      https_server()->GetURL("a.test", "/fenced_frames/title1.html"));
+  RenderFrameHost* fenced_rfh_generic =
+      fenced_frame_test_helper().CreateFencedFrame(
+          shell()->web_contents()->GetPrimaryMainFrame(), fenced_url);
+  ASSERT_TRUE(fenced_rfh_generic);
+  RenderFrameHostImpl* fenced_rfh =
+      static_cast<RenderFrameHostImpl*>(fenced_rfh_generic);
+
+  RenderProcessHostBadIpcMessageWaiter kill_waiter(fenced_rfh->GetProcess());
+
+  mojo::Receiver<blink::mojom::BrowserInterfaceBroker>& broker_receiver =
+      fenced_rfh->browser_interface_broker_receiver_for_testing();
+  blink::mojom::BrowserInterfaceBroker* broker =
+      broker_receiver.internal_state()->impl();
+
+  mojo::Remote<device::mojom::BatteryMonitor> remote;
+  broker->GetInterface(remote.BindNewPipeAndPassReceiver());
+
+  EXPECT_EQ(bad_message::BIBI_BIND_BATTERY_MONITOR_FOR_FENCED_FRAME,
+            kill_waiter.Wait());
+}
+
 namespace {
 
 // Interceptor that replaces the origin in the DidCommitProvisionalLoadParams
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.