Chrome · UI
CVE-2026-113244
Input Validation in UI
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Fcontent/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.cccontent/browser/bad_message.htools/metrics/histograms/metadata/stability/enums.xmlcontent/browser/browser_interface_binders.cc
Patch
From 0041349ae316cf2183a70c9d444b4281151d5ecb Mon Sep 17 00:00:00 2001 From: Benjamin Keen <[email protected]> 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 <[email protected]> Reviewed-by: Giovanni Ortuno Urquidi <[email protected]> Reviewed-by: Camille Lamy <[email protected]> Commit-Queue: Benjamin Keen <[email protected]> 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.
References
On This Page