SkRegion: change serialization unit tests, better validation code
Also: Don't alloc before validation. Change-Id: Ic2e007ecf4e06fb099366295b963f66df3f7903a Reviewed-on: https://skia-review.googlesource.com/8728 Reviewed-by: Mike Reed <reed@google.com> Commit-Queue: Hal Canary <halcanary@google.com>
This commit is contained in:
parent
5d9f3bfd2f
commit
58a1ea8d54
@ -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
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user