Drop SkTextBlobs with > 2M glyphs.

This will guard against buffer overflows
for large text blobs.

Bug: chromium:1080481
Change-Id: I13a10869babfa149a70c2f4caebb3a1ae4452b77
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/291456
Commit-Queue: Herb Derby <herb@google.com>
Reviewed-by: Mike Reed <reed@google.com>
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index af6b269..0802cd6 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -2629,6 +2629,19 @@
     TRACE_EVENT0("skia", TRACE_FUNC);
     RETURN_ON_NULL(blob);
     RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
+
+    // Overflow if more than 2^21 glyphs stopping a buffer overflow latter in the stack.
+    // See chromium:1080481
+    // TODO: can consider unrolling a few at a time if this limit becomes a problem.
+    int totalGlyphCount = 0;
+    constexpr int kMaxGlyphCount = 1 << 21;
+    SkTextBlob::Iter i(*blob);
+    SkTextBlob::Iter::Run r;
+    while (i.next(&r)) {
+        int glyphsLeft = kMaxGlyphCount - totalGlyphCount;
+        RETURN_ON_FALSE(r.fGlyphCount <= glyphsLeft);
+        totalGlyphCount += r.fGlyphCount;
+    }
     this->onDrawTextBlob(blob, x, y, paint);
 }
 
diff --git a/tests/TextBlobCacheTest.cpp b/tests/TextBlobCacheTest.cpp
index 62b60e9..bdf3baf 100644
--- a/tests/TextBlobCacheTest.cpp
+++ b/tests/TextBlobCacheTest.cpp
@@ -230,6 +230,48 @@
     return builder.make();
 }
 
+static sk_sp<SkTextBlob> make_large_blob() {
+    auto tf = SkTypeface::MakeFromName("Roboto2-Regular", SkFontStyle());
+    SkFont font;
+    font.setTypeface(tf);
+    font.setSubpixel(false);
+    font.setEdging(SkFont::Edging::kAlias);
+    font.setSize(24);
+
+    const int mallocSize = 0x3c3c3bd; // x86 size
+    std::unique_ptr<char[]> text{new char[mallocSize + 1]};
+    if (text == nullptr) {
+        return nullptr;
+    }
+    for (int i = 0; i < mallocSize; i++) {
+        text[i] = 'x';
+    }
+    text[mallocSize] = 0;
+
+    static const int maxGlyphLen = mallocSize;
+    std::unique_ptr<SkGlyphID[]> glyphs{new SkGlyphID[maxGlyphLen]};
+    int glyphCount =
+            font.textToGlyphs(
+                    text.get(), mallocSize, SkTextEncoding::kUTF8, glyphs.get(), maxGlyphLen);
+    SkTextBlobBuilder builder;
+    const auto& runBuffer = builder.allocRun(font, glyphCount, 0, 0);
+    for (int i = 0; i < glyphCount; i++) {
+        runBuffer.glyphs[i] = glyphs[i];
+    }
+    return builder.make();
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TextBlobIntegerOverflowTest, reporter, ctxInfo) {
+    auto grContext = ctxInfo.grContext();
+    const SkImageInfo info =
+            SkImageInfo::Make(kScreenDim, kScreenDim, kN32_SkColorType, kPremul_SkAlphaType);
+    auto surface = SkSurface::MakeRenderTarget(grContext, SkBudgeted::kNo, info);
+
+    auto blob = make_large_blob();
+    int y = 40;
+    SkBitmap base = draw_blob(blob.get(), surface.get(), {40, y + 0.0f});
+}
+
 static const bool kDumpPngs = true;
 // dump pngs needs a "good" and a "bad" directory to put the results in. This allows the use of the
 // skdiff tool to visualize the differences.