diff options
-rw-r--r-- | src/core/SkRegion.cpp | 221 | ||||
-rw-r--r-- | tests/RegionTest.cpp | 107 |
2 files changed, 164 insertions, 164 deletions
diff --git a/src/core/SkRegion.cpp b/src/core/SkRegion.cpp index dca9b9dda3..e873ebb453 100644 --- a/src/core/SkRegion.cpp +++ b/src/core/SkRegion.cpp @@ -1129,37 +1129,113 @@ size_t SkRegion::writeToMemory(void* storage) const { return buffer.pos(); } +// Validate that a memory sequence is a valid region. +// Try to check all possible errors. +// never read beyond &runs[runCount-1]. +static bool validate_run(const int32_t* runs, + int runCount, + const SkIRect& givenBounds, + int32_t ySpanCount, + int32_t intervalCount) { + // Region Layout: + // Top ( Bottom Span_Interval_Count ( Left Right )* Sentinel )+ Sentinel + if (ySpanCount < 1 || intervalCount < 2 || runCount != 2 + 3 * ySpanCount + 2 * intervalCount) { + return false; + } + SkASSERT(runCount >= 7); // 7==SkRegion::kRectRegionRuns + // quick sanity check: + if (runs[runCount - 1] != SkRegion::kRunTypeSentinel || + runs[runCount - 2] != SkRegion::kRunTypeSentinel) { + return false; + } + const int32_t* const end = runs + runCount; + SkIRect bounds = {0, 0, 0 ,0}; // calulated bounds + SkIRect rect = {0, 0, 0, 0}; // current rect + rect.fTop = *runs++; + if (rect.fTop == SkRegion::kRunTypeSentinel) { + return false; // no rect can contain SkRegion::kRunTypeSentinel + } + do { + --ySpanCount; + if (ySpanCount < 0) { + return false; // too many yspans + } + rect.fBottom = *runs++; + if (rect.fBottom == SkRegion::kRunTypeSentinel) { + return false; + } + int32_t xIntervals = *runs++; + SkASSERT(runs < end); + if (xIntervals < 0 || runs + 1 + 2 * xIntervals > end) { + return false; + } + intervalCount -= xIntervals; + if (intervalCount < 0) { + return false; // too many intervals + } + while (xIntervals-- > 0) { + rect.fLeft = *runs++; + rect.fRight = *runs++; + if (rect.fLeft == SkRegion::kRunTypeSentinel || + rect.fRight == SkRegion::kRunTypeSentinel || rect.isEmpty()) { + return false; + } + bounds.join(rect); + } + if (*runs++ != SkRegion::kRunTypeSentinel) { + return false; // required check sentinal. + } + rect.fTop = rect.fBottom; + SkASSERT(runs < end); + } while (*runs != SkRegion::kRunTypeSentinel); + ++runs; + if (ySpanCount != 0 || intervalCount != 0 || givenBounds != bounds) { + return false; + } + SkASSERT(runs == end); // if ySpanCount && intervalCount are right, must be correct length. + return true; +} size_t SkRegion::readFromMemory(const void* storage, size_t length) { SkRBuffer buffer(storage, length); SkRegion tmp; int32_t count; - if (buffer.readS32(&count) && (count >= 0) && buffer.read(&tmp.fBounds, sizeof(tmp.fBounds))) { - if (tmp.fBounds.isEmpty()) { - return 0; // bad bounds for non-empty region; report failure + // Serialized Region Format: + // Empty: + // -1 + // Simple Rect: + // 0 LEFT TOP RIGHT BOTTOM + // Complex Region: + // COUNT LEFT TOP RIGHT BOTTOM Y_SPAN_COUNT TOTAL_INTERVAL_COUNT [RUNS....] + if (!buffer.readS32(&count) || count < -1) { + return 0; + } + if (count >= 0) { + if (!buffer.read(&tmp.fBounds, sizeof(tmp.fBounds)) || tmp.fBounds.isEmpty()) { + return 0; // Short buffer or bad bounds for non-empty region; report failure. } if (count == 0) { tmp.fRunHead = SkRegion_gRectRunHeadPtr; } else { int32_t ySpanCount, intervalCount; - if (buffer.readS32(&ySpanCount) && buffer.readS32(&intervalCount) && - intervalCount > 1) { - tmp.allocateRuns(count, ySpanCount, intervalCount); - if (!tmp.isComplex()) { - return 0; // report failure - } - buffer.read(tmp.fRunHead->writable_runs(), count * sizeof(RunType)); - } else { - return 0; // report failure; + if (!buffer.readS32(&ySpanCount) || + !buffer.readS32(&intervalCount) || + buffer.available() < count * sizeof(int32_t)) { + return 0; + } + if (!validate_run((const int32_t*)((const char*)storage + buffer.pos()), count, + tmp.fBounds, ySpanCount, intervalCount)) { + return 0; // invalid runs, don't even allocate } + tmp.allocateRuns(count, ySpanCount, intervalCount); + SkASSERT(tmp.isComplex()); + SkAssertResult(buffer.read(tmp.fRunHead->writable_runs(), count * sizeof(int32_t))); } } - size_t sizeRead = 0; - if (buffer.isValid() && tmp.isValid()) { - this->swap(tmp); - sizeRead = buffer.pos(); - } - return sizeRead; + SkASSERT(tmp.isValid()); + SkASSERT(buffer.isValid()); + this->swap(tmp); + return buffer.pos(); } /////////////////////////////////////////////////////////////////////////////// @@ -1171,110 +1247,19 @@ const SkRegion& SkRegion::GetEmptyRegion() { /////////////////////////////////////////////////////////////////////////////// -// Starts with first X-interval, and returns a ptr to the X-sentinel -static const SkRegion::RunType* skip_intervals_slow(const SkRegion::RunType runs[]) { - // want to track that our intevals are all disjoint, such that - // prev-right < next-left. We rely on this optimization in places such as - // contains(). - // - SkRegion::RunType prevR = -SkRegion::kRunTypeSentinel; - - while (runs[0] < SkRegion::kRunTypeSentinel) { - if (prevR >= runs[0] - || runs[0] >= runs[1] - || runs[1] >= SkRegion::kRunTypeSentinel) { - return nullptr; - } - prevR = runs[1]; - runs += 2; - } - return runs; -} - -static bool compute_bounds(const SkRegion::RunType runs[], - SkIRect* bounds, int* ySpanCountPtr, - int* intervalCountPtr) { - SkASSERT(bounds && ySpanCountPtr && intervalCountPtr); - if (SkRegionValueIsSentinel(runs[0])) { return false; } - - int left = SK_MaxS32; - int rite = SK_MinS32; - int bot; - int ySpanCount = 0; - int intervalCount = 0; - - if (!runs) { return false; } - bounds->fTop = *runs++; - do { - bot = *runs++; - if (SkRegion::kRunTypeSentinel <= bot) { return false; } - - ySpanCount += 1; - - runs += 1; // skip intervalCount for now - if (*runs < SkRegion::kRunTypeSentinel) { - if (left > *runs) { - left = *runs; - } - - const SkRegion::RunType* prev = runs; - runs = skip_intervals_slow(runs); - if (!runs) { return false; } - int intervals = SkToInt((runs - prev) >> 1); - if (prev[-1] != intervals) { return false; } - intervalCount += intervals; - - if (rite < runs[-1]) { - rite = runs[-1]; - } - } else { // no intervals - if (0 != runs[-1]) { - return false; - } - } - if (SkRegion::kRunTypeSentinel != *runs) { - return false; - } - runs += 1; - } while (SkRegion::kRunTypeSentinel != *runs); - - bounds->fLeft = left; - bounds->fRight = rite; - bounds->fBottom = bot; - *ySpanCountPtr = ySpanCount; - *intervalCountPtr = intervalCount; - return true; -} - bool SkRegion::isValid() const { if (this->isEmpty()) { - // check for explicit empty (the zero rect), so we can compare rects to know when - // two regions are equal (i.e. emptyRectA == emptyRectB) - // this is stricter than just asserting fBounds.isEmpty() return fBounds == SkIRect{0, 0, 0, 0}; - } else { - if (fBounds.isEmpty()) { - return false; - } - if (!this->isRect()) { - if (!fRunHead - || fRunHead->fRefCnt < 1 - || fRunHead->fRunCount <= kRectRegionRuns) { - return false; - } - const RunType* run = fRunHead->readonly_runs(); - // check that our bounds match our runs - SkIRect bounds; - int ySpanCount, intervalCount; - return compute_bounds(run, &bounds, &ySpanCount, &intervalCount) - && bounds == fBounds - && ySpanCount > 0 - && fRunHead->getYSpanCount() == ySpanCount - && fRunHead->getIntervalCount() == intervalCount; - } else { - return true; - } } + if (fBounds.isEmpty()) { + return false; + } + if (this->isRect()) { + return true; + } + return fRunHead && fRunHead->fRefCnt > 0 && + validate_run(fRunHead->readonly_runs(), fRunHead->fRunCount, fBounds, + fRunHead->getYSpanCount(), fRunHead->getIntervalCount()); } #ifdef SK_DEBUG diff --git a/tests/RegionTest.cpp b/tests/RegionTest.cpp index f2a3d02787..2a5d0a5059 100644 --- a/tests/RegionTest.cpp +++ b/tests/RegionTest.cpp @@ -295,6 +295,17 @@ DEF_TEST(Region_writeToMemory, r) { REPORTER_ASSERT(r, nonEmpty); REPORTER_ASSERT(r, region.isComplex()); test_write(region, r); + + SkRegion complexRegion; + Union(&complexRegion, SkIRect::MakeXYWH(0, 0, 1, 1)); + Union(&complexRegion, SkIRect::MakeXYWH(0, 0, 3, 3)); + Union(&complexRegion, SkIRect::MakeXYWH(10, 0, 3, 3)); + Union(&complexRegion, SkIRect::MakeXYWH(0, 10, 13, 3)); + test_write(complexRegion, r); + + Union(&complexRegion, SkIRect::MakeXYWH(10, 20, 3, 3)); + Union(&complexRegion, SkIRect::MakeXYWH(0, 20, 3, 3)); + test_write(complexRegion, r); } DEF_TEST(Region_readFromMemory_bad, r) { @@ -302,50 +313,54 @@ DEF_TEST(Region_readFromMemory_bad, r) { // and might need to remove or change some of these tests. SkRegion region; - static const char data0[] = - "\2\0\0\0\277]\345\222\\\2G\252\0\177'\10\203\236\211>\377\340@\351" - "!\370y\3\31\232r\353\343\336Ja\177\377\377\377\244\301\362:Q\\\0\0" - "\1\200\263\214\374\276\336P\225^\230\20UH N\265\357\177\240\0\306\377" - "\177\346\222S \0\375\0\332\247 \302I\240H\374\200lk\r`\0375\324W\215" - "\270tE^,\224n\310fy\377\231AH\16\235A\371\315\347\360\265\372r\232" - "\301\216\35\227:\265]\32\20W\263yc\207\246\270tE^,\224n\310sy\2\0A" - "\14\241SQ\\\303\364\0\0\1\200\0\0\374k\r`\0375\324Wp\270\267\313\313" - "\313\313\313@\277\365b\341\343\336Ja\357~\263\0\2\333\260\220\\\303" - "\364\265\332\267\242\325nlX\367\27I4444;\266\256\37/M\207"; - size_t data0length = 221; - REPORTER_ASSERT(r, 0 == region.readFromMemory(data0, data0length)); - - static const char data1[] = - "\2\0\0\0\\\2G\252\0\177'\10\247 \302I\240H\374\200lk\r`\0375\324Wr" - "\232\301\216\35\227:\265]\32\20W\263yc\207\246\270tE^,\224n\310sy\2" - "\0A\14\241SQ\\\303\364\0\0\1\200\0\0\374k\r`\0375\324Wp\270\267\313" - "\313\313\313\313@\277\365b\341\343\336Ja\357~\263\0\2\333\260\220\\" - "\303\364\265\332\267\242\325nlX\367\27I4444;\266\256\37/M\207"; - size_t data1length = 129; - REPORTER_ASSERT(r, 0 == region.readFromMemory(data1, data1length)); - - static const char data2[] = - " \0\0\0`\6\363\234AH\26\235\0\0\0\0\251\217\27I\27C\361,\320u\3171" - "\10.\206\277]\345\222\334\2C\252\242a'\10\251\31\326\372\334A\277\30" - "\240M\275v\201\271\3527\215{)S\3771{\345Z\250\23\213\331\23j@\13\220" - "\200Z^-\20\212=;\355\314\36\260c\224M\16\271Szy\373\204M\21\177\251" - "\275\r\274M\370\201\243^@\343\236JaS\204\3212\244\301\327\22\352KI" - "\207\350z\300\250\372\26\14\2\233K\330\16\251\230\223\r\"\243\271\17" - ")\260\262\2[a.*.4\14\344\307\350\3\0\0-\350G!\31\300\205\205\205\205" - "\205\205\205\205\205\205\205\205\205\205\205\205\305m\311<Q\347\30" - "\324\203f\2614\3115\206\214@:\346n\254\37\225\263\214\374\276\336\23" - "\270\304\262\25\24_\342\223\253\351L\30\372\373\243\240g\0367V\336" - "P\7-1{\345Z\250\23\213P\225^\230\27UH\206N\265\357\177\262\302\306" - "kk\7\233\234N\32@\355H\327\34\337\0V\30 \225\35\225\233\253\0144>\310" - "\352\346L\232\215\270t[^,\224l\312f\2025?}\1ZL\217wf8C\346\222S\240" - "\203\375\374\332\247 \302I\271H\0\0lk\22`\0375\324W\374\265\342\243" - "yL\211\215\270tE^,\224l\312f\2025?}\1ZL\217wf8C\333\370_.\277A\277" - "^\\\313!\342\340\213\210\244\272\33\275\360\301\347\315\377\6a\272" - "kyi:W\332\366\5\312F\217c\243\20,\"\240\347o\375\277\317}HEji\367\374" - "\331\214\314\242x\356\340\350\362r$\222\266\325\201\234\267P\243N\361" - "++++++++\370+@++\205!8B\255L\3\3416\335$\\\r\265W[F\326\316w{.\306" - ">f2i\244\242=Y\236\364\302\357xR:Q\\\303\364\265\332\200\242\325nl" - "X\373\307\5<-"; - size_t data2length = 512; - REPORTER_ASSERT(r, 0 == region.readFromMemory(data2, data2length)); + { + // invalid boundary rectangle + int32_t data[5] = {0, 4, 4, 8, 2}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } + // Region Layout, Serialized Format: + // COUNT LEFT TOP RIGHT BOTTOM Y_SPAN_COUNT TOTAL_INTERVAL_COUNT + // Top ( Bottom Span_Interval_Count ( Left Right )* Sentinel )+ Sentinel + { + // Example of valid data + int32_t data[] = {9, 0, 0, 10, 10, 1, 2, 0, 10, 2, 0, 4, 6, 10, + 2147483647, 2147483647}; + REPORTER_ASSERT(r, 0 != region.readFromMemory(data, sizeof(data))); + } + { + // Short count + int32_t data[] = {8, 0, 0, 10, 10, 1, 2, 0, 10, 2, 0, 4, 6, 10, + 2147483647, 2147483647}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } + { + // bounds don't match + int32_t data[] = {9, 0, 0, 10, 11, 1, 2, 0, 10, 2, 0, 4, 6, 10, + 2147483647, 2147483647}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } + { + // bad yspan count + int32_t data[] = {9, 0, 0, 10, 10, 2, 2, 0, 10, 2, 0, 4, 6, 10, + 2147483647, 2147483647}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } + { + // bad int count + int32_t data[] = {9, 0, 0, 10, 10, 1, 3, 0, 10, 2, 0, 4, 6, 10, + 2147483647, 2147483647}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } + { + // bad final sentinal + int32_t data[] = {9, 0, 0, 10, 10, 1, 2, 0, 10, 2, 0, 4, 6, 10, + 2147483647, -1}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } + { + // bad row sentinal + int32_t data[] = {9, 0, 0, 10, 10, 1, 2, 0, 10, 2, 0, 4, 6, 10, + -1, 2147483647}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } } |