diff --git a/src/core/SkPathRef.cpp b/src/core/SkPathRef.cpp index ef051f7e84..55ee9914fa 100644 --- a/src/core/SkPathRef.cpp +++ b/src/core/SkPathRef.cpp @@ -186,6 +186,38 @@ void SkPathRef::CreateTransformedCopy(sk_sp* dst, SkDEBUGCODE((*dst)->validate();) } +// Given the verb array, deduce the required number of pts and conics, +// or if an invalid verb is encountered, return false. +static bool deduce_pts_conics(const uint8_t verbs[], int vCount, int* ptCountPtr, + int* conicCountPtr) { + int ptCount = 0; + int conicCount = 0; + for (int i = 0; i < vCount; ++i) { + switch (verbs[i]) { + case SkPath::kMove_Verb: + case SkPath::kLine_Verb: + ptCount += 1; + break; + case SkPath::kConic_Verb: + conicCount += 1; + // fall-through + case SkPath::kQuad_Verb: + ptCount += 2; + break; + case SkPath::kCubic_Verb: + ptCount += 3; + break; + case SkPath::kClose_Verb: + break; + default: + return false; + } + } + *ptCountPtr = ptCount; + *conicCountPtr = conicCount; + return true; +} + SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer) { SkPathRef* ref = new SkPathRef; @@ -231,6 +263,17 @@ SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer) { delete ref; return nullptr; } + + // Check that the verbs are valid, and imply the correct number of pts and conics + { + int pCount, cCount; + if (!deduce_pts_conics(ref->verbsMemBegin(), ref->countVerbs(), &pCount, &cCount) || + pCount != ref->countPoints() || cCount != ref->fConicWeights.count()) { + delete ref; + return nullptr; + } + } + ref->fBoundsIsDirty = false; // resetToSize clears fSegmentMask and fIsOval diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp index 9a44d2932d..2059c36db3 100644 --- a/tests/PathTest.cpp +++ b/tests/PathTest.cpp @@ -2532,6 +2532,69 @@ static void write_and_read_back(skiatest::Reporter* reporter, REPORTER_ASSERT(reporter, origBounds == readBackBounds); } +static void test_corrupt_flattening(skiatest::Reporter* reporter) { + SkPath path; + path.moveTo(1, 2); + path.lineTo(1, 2); + path.quadTo(1, 2, 3, 4); + path.conicTo(1, 2, 3, 4, 0.5f); + path.cubicTo(1, 2, 3, 4, 5, 6); + uint8_t buffer[1024]; + SkDEBUGCODE(size_t size =) path.writeToMemory(buffer); + SkASSERT(size <= sizeof(buffer)); + + // find where the counts and verbs are stored : from the impl in SkPathRef.cpp + int32_t* vCount = (int32_t*)&buffer[16]; + SkASSERT(*vCount == 5); + int32_t* pCount = (int32_t*)&buffer[20]; + SkASSERT(*pCount == 9); + int32_t* cCount = (int32_t*)&buffer[24]; + SkASSERT(*cCount == 1); + uint8_t* verbs = &buffer[28]; + + REPORTER_ASSERT(reporter, path.readFromMemory(buffer, sizeof(buffer))); + + // check that we detect under/over-flow of counts + + *vCount += 1; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + *vCount -= 1; // restore + + *pCount += 1; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + *pCount -= 2; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + *pCount += 1; // restore + + *cCount += 1; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + *cCount -= 2; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + *cCount += 1; // restore + + // Check that we detect when the verbs indicate more or fewer pts/conics + + uint8_t save = verbs[0]; + SkASSERT(save == SkPath::kCubic_Verb); + verbs[0] = SkPath::kQuad_Verb; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + verbs[0] = save; + + save = verbs[1]; + SkASSERT(save == SkPath::kConic_Verb); + verbs[1] = SkPath::kQuad_Verb; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + verbs[1] = SkPath::kCubic_Verb; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + verbs[1] = save; + + // Check that we detect invalid verbs + save = verbs[1]; + verbs[1] = 17; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + verbs[1] = save; +} + static void test_flattening(skiatest::Reporter* reporter) { SkPath p; @@ -2580,6 +2643,8 @@ static void test_flattening(skiatest::Reporter* reporter) { write_and_read_back(reporter, oval); } + + test_corrupt_flattening(reporter); } static void test_transform(skiatest::Reporter* reporter) {