Low chrome Input Validation 🔧 Commit mapped

Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactImproper input validation in UI
DescriptionImproper input validation in UI
ComponentUI
Bug ClassInput Validation
Tracker514078112
Fix commit0041349ae316 (chromium/src) +90/-4
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-20

Changed Functions

FunctionChangeNotes
TEST_F
content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
modified

Files Changed

  • content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
  • content/browser/bad_message.h
  • tools/metrics/histograms/metadata/stability/enums.xml
  • content/browser/browser_interface_binders.cc
From 0041349ae316cf2183a70c9d444b4281151d5ecb Mon Sep 17 00:00:00 2001
From: Benjamin Keen <bkeen@google.com>
Date: Wed, 16 Sep 2026 11:08:16 -0700
Subject: [PATCH] Enforce permissions policy and fenced frame checks for video PiP

Ensure that the PictureInPictureService Mojo bindings are rejected if
requested from within a fenced frame, or when disallowed by the
picture-in-picture permissions policy.

Browser side user activation checks are omitted as per
content/SECURITY.md, which notes that missing user activation in the
browser is not currently treated as a security boundary.

Bug: 514078112
Change-Id: I23b19da86b5c0003ccbce132cd2eb55d9cd125d0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8407465
Reviewed-by: Tommy Steimel <steimel@chromium.org>
Reviewed-by: Giovanni Ortuno Urquidi <ortuno@chromium.org>
Reviewed-by: Camille Lamy <clamy@chromium.org>
Commit-Queue: Benjamin Keen <bkeen@google.com>
Cr-Commit-Position: refs/heads/main@{#1699085}
---
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h
index 568abf9..4ebb96b7 100644
--- a/content/browser/bad_message.h
+++ b/content/browser/bad_message.h
@@ -427,6 +427,8 @@
   RFPH_SYNCHRONIZE_VISUAL_PROPERTIES_WITHOUT_CPFC = 399,
   RFPH_UPDATE_VIEWPORT_INTERSECTION_WITHOUT_CPFC = 400,
   RFH_DEDICATED_WORKER_HOST_FACTORY_PDF_PROCESS_BLOCKED = 401,
+  BIBI_BIND_PICTURE_IN_PICTURE_SERVICE_FOR_FENCED_FRAME = 402,
+  BIBI_BIND_PICTURE_IN_PICTURE_SERVICE_BLOCKED_BY_PERMISSIONS_POLICY = 403,
 
   // 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 e43daedd..1d55322 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -828,6 +828,31 @@
       ->Bind(std::move(receiver));
 }
 
+void BindPictureInPictureService(
+    RenderFrameHost* host,
+    mojo::PendingReceiver<blink::mojom::PictureInPictureService> receiver) {
+  // Fenced frames are not allowed to use Picture-in-Picture.
+  if (host->IsNestedWithinFencedFrame()) {
+    bad_message::ReceivedBadMessage(
+        host->GetProcess(),
+        bad_message::BadMessageReason::
+            BIBI_BIND_PICTURE_IN_PICTURE_SERVICE_FOR_FENCED_FRAME);
+    return;
+  }
+
+  // Enforce Permissions Policy browser-side.
+  if (!host->IsFeatureEnabled(
+          network::mojom::PermissionsPolicyFeature::kPictureInPicture)) {
+    bad_message::ReceivedBadMessage(
+        host->GetProcess(),
+        bad_message::BadMessageReason::
+            BIBI_BIND_PICTURE_IN_PICTURE_SERVICE_BLOCKED_BY_PERMISSIONS_POLICY);
+    return;
+  }
+
+  PictureInPictureServiceImpl::Create(host, std::move(receiver));
+}
+
 template <auto Method, typename Interface>
 void BindRenderFrameHostImpl(RenderFrameHost* host,
                              mojo::PendingReceiver<Interface> receiver)
@@ -1392,8 +1417,7 @@
   map->Add<blink::mojom::KeyboardLockService>(
       &KeyboardLockServiceImpl::CreateMojoService);
   map->Add<blink::mojom::MediaSessionService>(&MediaSessionServiceImpl::Create);
-  map->Add<blink::mojom::PictureInPictureService>(
-      &PictureInPictureServiceImpl::Create);
+  map->Add<blink::mojom::PictureInPictureService>(&BindPictureInPictureService);
   map->Add<blink::mojom::WakeLockService>(&WakeLockServiceImpl::Create);
 #if BUILDFLAG(ENABLE_VR)
   map->Add<device::mojom::VRService>(&VRServiceImpl::Create);
diff --git a/tools/metrics/histograms/metadata/stability/enums.xml b/tools/metrics/histograms/metadata/stability/enums.xml
index e954265b..6b50f81e 100644
--- a/tools/metrics/histograms/metadata/stability/enums.xml
+++ b/tools/metrics/histograms/metadata/stability/enums.xml
@@ -587,6 +587,10 @@
   <int value="400" label="RFPH_UPDATE_VIEWPORT_INTERSECTION_WITHOUT_CPFC"/>
   <int value="401"
       label="RFH_DEDICATED_WORKER_HOST_FACTORY_PDF_PROCESS_BLOCKED"/>
+  <int value="402"
+      label="BIBI_BIND_PICTURE_IN_PICTURE_SERVICE_FOR_FENCED_FRAME"/>
+  <int value="403"
+      label="BIBI_BIND_PICTURE_IN_PICTURE_SERVICE_BLOCKED_BY_PERMISSIONS_POLICY"/>
 </enum>
 
 <enum name="BadMessageReasonExtensions">
diff --git a/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc b/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
index 11890ea..1b82a1d 100644
--- a/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
+++ b/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
@@ -11,11 +11,14 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "build/build_config.h"
+#include "content/browser/bad_message.h"
 #include "content/browser/picture_in_picture/video_picture_in_picture_window_controller_impl.h"
 #include "content/public/browser/overlay_window.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/common/content_client.h"
+#include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_content_browser_client.h"
 #include "content/test/test_render_frame_host.h"
 #include "content/test/test_render_view_host.h"
@@ -28,8 +31,7 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "mojo/public/cpp/test_support/fake_message_dispatch_context.h"
-#include "mojo/public/cpp/test_support/test_utils.h"
+#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/mojom/frame/fullscreen.mojom.h"
 #include "third_party/blink/public/mojom/picture_in_picture/picture_in_picture.mojom.h"
@@ -982,4 +984,58 @@
   EXPECT_FALSE(session_remote_out);
 }
 
+TEST_F(PictureInPictureServiceImplTest, Bind_BlockedByPermissionsPolicy) {
+  // Create a child frame where picture-in-picture is disallowed by Permissions
+  // Policy.
+  network::ParsedPermissionsPolicy permissions_policy;
+  permissions_policy.emplace_back(
+      network::mojom::PermissionsPolicyFeature::kPictureInPicture);
+  TestRenderFrameHost* child_rfh =
+      main_test_rfh()->AppendChildWithPolicy("subframe", permissions_policy);
+  auto navigation = NavigationSimulator::CreateRendererInitiated(
+      GURL("https://example.com"), child_rfh);
+  navigation->Commit();
+
+  ASSERT_FALSE(child_rfh->IsFeatureEnabled(
+      network::mojom::PermissionsPolicyFeature::kPictureInPicture));
+
+  base::HistogramTester histogram_tester;
+  mojo::Remote<blink::mojom::PictureInPictureService> service_remote;
+  child_rfh->browser_interface_broker_receiver_for_testing()
+      .internal_state()
+      ->impl()
+      ->GetInterface(service_remote.BindNewPipeAndPassReceiver());
+
+  EXPECT_EQ(1, child_rfh->GetProcess()->bad_msg_count());
+  histogram_tester.ExpectUniqueSample(
+      "Stability.BadMessageTerminated.Content",
+      bad_message::BadMessageReason::
+          BIBI_BIND_PICTURE_IN_PICTURE_SERVICE_BLOCKED_BY_PERMISSIONS_POLICY,
+      1);
+}
+
+TEST_F(PictureInPictureServiceImplTest, Bind_BlockedForFencedFrame) {
+  TestRenderFrameHost* fenced_frame_rfh = main_test_rfh()->AppendFencedFrame();
+  auto navigation = NavigationSimulator::CreateRendererInitiated(
+      GURL("https://example.com"), fenced_frame_rfh);
+  navigation->Commit();
+  fenced_frame_rfh =
+      static_cast<TestRenderFrameHost*>(navigation->GetFinalRenderFrameHost());
+  ASSERT_TRUE(fenced_frame_rfh->IsNestedWithinFencedFrame());
+
+  base::HistogramTester histogram_tester;
+  mojo::Remote<blink::mojom::PictureInPictureService> service_remote;
+  fenced_frame_rfh->browser_interface_broker_receiver_for_testing()
+      .internal_state()
+      ->impl()
+      ->GetInterface(service_remote.BindNewPipeAndPassReceiver());
+
+  EXPECT_EQ(1, fenced_frame_rfh->GetProcess()->bad_msg_count());
+  histogram_tester.ExpectUniqueSample(
+      "Stability.BadMessageTerminated.Content",
+      bad_message::BadMessageReason::
+          BIBI_BIND_PICTURE_IN_PICTURE_SERVICE_FOR_FENCED_FRAME,
+      1);
+}
+
 }  // namespace content
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc b/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
index 11890ea..1b82a1d 100644
--- a/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
+++ b/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
@@ -11,11 +11,14 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "build/build_config.h"
+#include "content/browser/bad_message.h"
 #include "content/browser/picture_in_picture/video_picture_in_picture_window_controller_impl.h"
 #include "content/public/browser/overlay_window.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/common/content_client.h"
+#include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_content_browser_client.h"
 #include "content/test/test_render_frame_host.h"
 #include "content/test/test_render_view_host.h"
@@ -28,8 +31,7 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "mojo/public/cpp/test_support/fake_message_dispatch_context.h"
-#include "mojo/public/cpp/test_support/test_utils.h"
+#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/mojom/frame/fullscreen.mojom.h"
 #include "third_party/blink/public/mojom/picture_in_picture/picture_in_picture.mojom.h"
@@ -982,4 +984,58 @@
   EXPECT_FALSE(session_remote_out);
 }
 
+TEST_F(PictureInPictureServiceImplTest, Bind_BlockedByPermissionsPolicy) {
+  // Create a child frame where picture-in-picture is disallowed by Permissions
+  // Policy.
+  network::ParsedPermissionsPolicy permissions_policy;
+  permissions_policy.emplace_back(
+      network::mojom::PermissionsPolicyFeature::kPictureInPicture);
+  TestRenderFrameHost* child_rfh =
+      main_test_rfh()->AppendChildWithPolicy("subframe", permissions_policy);
+  auto navigation = NavigationSimulator::CreateRendererInitiated(
+      GURL("https://example.com"), child_rfh);
+  navigation->Commit();
+
+  ASSERT_FALSE(child_rfh->IsFeatureEnabled(
+      network::mojom::PermissionsPolicyFeature::kPictureInPicture));
+
+  base::HistogramTester histogram_tester;
+  mojo::Remote<blink::mojom::PictureInPictureService> service_remote;
+  child_rfh->browser_interface_broker_receiver_for_testing()
+      .internal_state()
+      ->impl()
+      ->GetInterface(service_remote.BindNewPipeAndPassReceiver());
+
+  EXPECT_EQ(1, child_rfh->GetProcess()->bad_msg_count());
+  histogram_tester.ExpectUniqueSample(
+      "Stability.BadMessageTerminated.Content",
+      bad_message::BadMessageReason::
+          BIBI_BIND_PICTURE_IN_PICTURE_SERVICE_BLOCKED_BY_PERMISSIONS_POLICY,
+      1);
+}
+
+TEST_F(PictureInPictureServiceImplTest, Bind_BlockedForFencedFrame) {
+  TestRenderFrameHost* fenced_frame_rfh = main_test_rfh()->AppendFencedFrame();
+  auto navigation = NavigationSimulator::CreateRendererInitiated(
+      GURL("https://example.com"), fenced_frame_rfh);
+  navigation->Commit();
+  fenced_frame_rfh =
+      static_cast<TestRenderFrameHost*>(navigation->GetFinalRenderFrameHost());
+  ASSERT_TRUE(fenced_frame_rfh->IsNestedWithinFencedFrame());
+
+  base::HistogramTester histogram_tester;
+  mojo::Remote<blink::mojom::PictureInPictureService> service_remote;
+  fenced_frame_rfh->browser_interface_broker_receiver_for_testing()
+      .internal_state()
+      ->impl()
+      ->GetInterface(service_remote.BindNewPipeAndPassReceiver());
+
+  EXPECT_EQ(1, fenced_frame_rfh->GetProcess()->bad_msg_count());
+  histogram_tester.ExpectUniqueSample(
+      "Stability.BadMessageTerminated.Content",
+      bad_message::BadMessageReason::
+          BIBI_BIND_PICTURE_IN_PICTURE_SERVICE_FOR_FENCED_FRAME,
+      1);
+}
+
 }  // namespace content
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.