diff --git a/src/core/SkEdgeClipper.cpp b/src/core/SkEdgeClipper.cpp index 7a09ca30af..adca5ab7f5 100644 --- a/src/core/SkEdgeClipper.cpp +++ b/src/core/SkEdgeClipper.cpp @@ -17,6 +17,14 @@ #include #include +// === POC INSTRUMENTATION === +#include +#include +#include +#include +static std::atomic g_maxVerbs{0}, g_maxPts{0}; +// === END POC INSTRUMENTATION === + static bool quick_reject(const SkRect& bounds, const SkRect& clip) { return bounds.fTop >= clip.fBottom || bounds.fBottom <= clip.fTop; } @@ -446,6 +454,24 @@ bool SkEdgeClipper::clipCubic(const SkPoint srcPts[4], const SkRect& clip) { } } + // === POC INSTRUMENTATION === + { + int nv = (int)(fCurrVerb - fVerbs); + int np = (int)(fCurrPoint - fPoints); + int pv = g_maxVerbs.load(); + int pp = g_maxPts.load(); + if (nv > pv || np > pp) { + if (nv > pv) g_maxVerbs.store(nv); + if (np > pp) g_maxPts.store(np); + fprintf(stderr, + "[SkEdgeClipper] verbs=%d pts=%d P=(%a,%a)(%a,%a)(%a,%a)(%a,%a) clip=[%a,%a,%a,%a]\n", + nv, np, + srcPts[0].fX, srcPts[0].fY, srcPts[1].fX, srcPts[1].fY, + srcPts[2].fX, srcPts[2].fY, srcPts[3].fX, srcPts[3].fY, + clip.fLeft, clip.fTop, clip.fRight, clip.fBottom); + } + } + // === END POC INSTRUMENTATION === fCurrVerbStop = fCurrVerb; fCurrPoint = fPoints; fCurrVerb = fVerbs; @@ -600,3 +626,181 @@ void SkEdgeClipper::ClipPath(const SkPathRaw& raw, const SkRect& clip, bool canC } } } + +// === POC FUZZ HARNESS === +// Exported fuzzer for SkEdgeClipper::clipCubic. ASAN will catch any +// stack-buffer-overflow on fPoints[54]/fVerbs[18]. +extern "C" __attribute__((visibility("default"))) +void SkEdgeClipper_FuzzCubic(uint64_t seed, uint64_t iters) { + auto u2f = [](uint32_t u) { float f; memcpy(&f, &u, 4); return f; }; + auto f2u = [](float f) { uint32_t u; memcpy(&u, &f, 4); return u; }; + std::mt19937_64 rng(seed); + + SkPoint bestP[4] = {}; + SkRect bestC = {}; + int bestPts = 0; + + for (uint64_t i = 0; i < iters; i++) { + SkPoint p[4]; + SkRect c; + bool cull; + int strat = rng() % 8; + + auto rand_float = [&](float lo, float hi) { + return lo + (float)((double)(rng() >> 11) * (1.0/9007199254740992.0)) * (hi - lo); + }; + auto rand_finite = [&]() { + for (;;) { + float f = u2f((uint32_t)rng()); + if (std::isfinite(f) && std::fabs(f) < (1 << 22)) return f; + } + }; + auto tiny = [&]() { + uint32_t u = ((uint32_t)rng() & 0x007FFFFF) | (((uint32_t)rng() % 5) << 23); + if (rng() & 1) u |= 0x80000000; + return u2f(u); + }; + + cull = (rng() & 1); + + switch (strat) { + case 0: { + // x==y degenerate (found 7 pieces in offline search) + float a = rand_float(50.f, 4e6f * 0.9f); + float ex = tiny(), ey = tiny(); + p[0] = {a, a}; p[1] = {-a, -a}; p[2] = {a, a}; p[3] = {ex, ey}; + if (rng() & 1) std::swap(p[0], p[3]); + if (rng() & 1) { std::swap(p[0], p[1]); std::swap(p[2], p[3]); } + float cl = rand_float(-a/2, a/2); + c = SkRect::MakeLTRB(cl, -1e7f, cl + rand_float(0.001f, a/4), 1e7f); + break; + } + case 1: { + // random bit patterns + for (int k = 0; k < 4; k++) p[k] = {rand_finite(), rand_finite()}; + float cl = rand_float(-100, 100); + c = SkRect::MakeLTRB(cl, -4e6f, cl + rand_float(0.001f, 10.f), 4e6f); + break; + } + case 2: { + // subnormal/tiny mix (crbug/698714 style) + float big = rand_float(100.f, 300.f); + for (int k = 0; k < 4; k++) { + p[k].fX = (rng() & 1) ? tiny() : ((rng() & 1) ? big : -big) + tiny(); + p[k].fY = (rng() & 1) ? tiny() : ((rng() & 1) ? big : -big) + tiny(); + } + float cl = rand_float(-10, 10); + c = SkRect::MakeLTRB(cl, -1e6f, cl + rand_float(0.01f, 65.f), 1e6f); + break; + } + case 3: { + // Y/X near-equal extrema; X oscillating + float a = rand_float(1.f, 1000.f); + float s = rand_float(100.f, 3e6f); + p[0] = {-s + tiny(), tiny()}; + p[1] = {3*s + tiny(), a}; + p[2] = {-3*s + tiny(), -a}; + p[3] = {s + tiny(), tiny()}; + float cl = rand_float(-2, 2); + c = SkRect::MakeLTRB(cl, -1e7f, cl + rand_float(0.001f, 1.f), 1e7f); + break; + } + case 4: { + // narrow clip in Y too (exercise chop_cubic_in_Y) + for (int k = 0; k < 4; k++) p[k] = {rand_finite(), rand_finite()}; + float cl = rand_float(-100, 100), ct = rand_float(-100, 100); + c = SkRect::MakeLTRB(cl, ct, cl + rand_float(1.f, 10.f), ct + rand_float(1.f, 10.f)); + break; + } + case 5: { + // integer clip rect (matches SkEdgeBuilder reality) + for (int k = 0; k < 4; k++) p[k] = {rand_finite(), rand_finite()}; + int cl = (int)(rng() % 200) - 100; + int ct = (int)(rng() % 200) - 100; + int cw = 1 + (int)(rng() % 100); + int ch = 1 + (int)(rng() % 100); + c = SkRect::MakeLTRB(cl, ct, cl + cw, ct + ch); + break; + } + case 6: { + // mutate best + if (bestPts == 0) continue; + memcpy(p, bestP, sizeof(p)); c = bestC; + int n = 1 + (rng() % 3); + for (int m = 0; m < n; m++) { + int idx = rng() % 12; + float* f = (idx < 8) ? (&p[0].fX + idx) : (&c.fLeft + (idx - 8)); + uint32_t u = f2u(*f); + if (rng() & 1) u ^= (1u << (rng() % 23)); + else u += (int)(rng() % 21) - 10; + float nf = u2f(u); + if (std::isfinite(nf)) *f = nf; + } + if (c.fLeft >= c.fRight || c.fTop >= c.fBottom) continue; + break; + } + case 7: { + // crbug/698714 inputs, mutated + static const uint32_t base[5][8] = { + {0xb5434343,0x434300be,0x43434343,0x43434341,0xb74343bd,0x01434343,0x10434343,0x00144332}, + {0x10434343,0x00144332,0x016037c0,0x43434343,0x43434343,0x43434343,0xb5434343,0x43434343}, + {0xb5434343,0x43434343,0x43434344,0x43434341,0xb74343bd,0x01434343,0x43434343,0xc0000014}, + {0x43434343,0xc0000014,0xcab33535,0x34cacaca,0x2acacaca,0xcacacae3,0x27481927,0x27334805}, + {0xb5434343,0x43434343,0x43434343,0x43434341,0xb74343b9,0x43433643,0x43434343,0xc0000014}, + }; + int b = rng() % 5; + for (int k = 0; k < 4; k++) { + uint32_t ux = base[b][k*2], uy = base[b][k*2+1]; + if (rng() % 4 == 0) ux ^= (1u << (rng() % 24)); + if (rng() % 4 == 0) uy ^= (1u << (rng() % 24)); + p[k] = {u2f(ux), u2f(uy)}; + } + int cl = (int)(rng() % 100), cw = 1 + (int)(rng() % 200); + c = SkRect::MakeLTRB(cl, 0, cl + cw, 202 + (int)(rng()%100)); + break; + } + } + + bool finite = true; + for (int k = 0; k < 4; k++) + if (!std::isfinite(p[k].fX) || !std::isfinite(p[k].fY)) finite = false; + if (!finite) continue; + + int prevPts = g_maxPts.load(); + SkEdgeClipper clipper(cull); + clipper.clipCubic(p, c); // ASAN catches overflow here + int newPts = g_maxPts.load(); + if (newPts > prevPts) { + bestPts = newPts; + memcpy(bestP, p, sizeof(p)); + bestC = c; + } + } + fprintf(stderr, "[SkEdgeClipper_FuzzCubic] seed=%llu done. maxVerbs=%d maxPts=%d\n", + (unsigned long long)seed, g_maxVerbs.load(), g_maxPts.load()); +} + +// Run fuzzer at load time if SKEC_FUZZ env var is set. +namespace { +struct SkEdgeClipperFuzzRunner { + SkEdgeClipperFuzzRunner() { + const char* e = getenv("SKEC_FUZZ"); + if (!e) return; + uint64_t iters = strtoull(e, nullptr, 0); + if (iters == 0) iters = 50000000; + const char* s = getenv("SKEC_SEED"); + uint64_t seed = s ? strtoull(s, nullptr, 0) : 1; + fprintf(stderr, "[SkEdgeClipper_FuzzCubic] starting seed=%llu iters=%llu\n", + (unsigned long long)seed, (unsigned long long)iters); + SkEdgeClipper_FuzzCubic(seed, iters); + // Also run a few more seeds + for (uint64_t k = 1; k < 4; k++) { + SkEdgeClipper_FuzzCubic(seed + k * 0x9E3779B97F4A7C15ULL, iters); + } + fprintf(stderr, "[SkEdgeClipper_FuzzCubic] FINAL maxVerbs=%d maxPts=%d (limits: 18, 54)\n", + g_maxVerbs.load(), g_maxPts.load()); + exit(0); + } +} g_skec_fuzz_runner; +} +// === END POC FUZZ HARNESS ===