Revert "Revert "Fix SkPathRef deserialization malloc crash""
This reverts commit a4ce4b1f6b
.
Fix SkPathRef deserialization malloc crash
If the path says it has more points/verbs/etc than the buffer could
be holding, then resetToSize could try to allocate something huge
and crash.
Bug: skia:
Change-Id: I23b8870e9f74386aca89fb8f9a60d3b452044094
Reviewed-on: https://skia-review.googlesource.com/26805
Commit-Queue: Mike Klein <mtklein@chromium.org>
Reviewed-by: Mike Klein <mtklein@chromium.org>
This commit is contained in:
parent
6cd51b51d6
commit
41a930f85b
@ -12,6 +12,7 @@
|
||||
#include "../private/SkPathRef.h"
|
||||
|
||||
class SkAutoPathBoundsUpdate;
|
||||
class SkData;
|
||||
class SkRRect;
|
||||
class SkWStream;
|
||||
|
||||
@ -1090,6 +1091,12 @@ public:
|
||||
* If buffer is NULL, it still returns the number of bytes.
|
||||
*/
|
||||
size_t writeToMemory(void* buffer) const;
|
||||
|
||||
/**
|
||||
* Write the path to a buffer. Can recreate the path by calling readFromMemory().
|
||||
*/
|
||||
sk_sp<SkData> serialize() const;
|
||||
|
||||
/**
|
||||
* Initializes the path from the buffer
|
||||
*
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <cmath>
|
||||
#include "SkBuffer.h"
|
||||
#include "SkCubicClipper.h"
|
||||
#include "SkData.h"
|
||||
#include "SkGeometry.h"
|
||||
#include "SkMath.h"
|
||||
#include "SkPathPriv.h"
|
||||
@ -2061,6 +2062,13 @@ size_t SkPath::writeToMemory(void* storage) const {
|
||||
return buffer.pos();
|
||||
}
|
||||
|
||||
sk_sp<SkData> SkPath::serialize() const {
|
||||
size_t size = this->writeToMemory(nullptr);
|
||||
sk_sp<SkData> data = SkData::MakeUninitialized(size);
|
||||
this->writeToMemory(data->writable_data());
|
||||
return data;
|
||||
}
|
||||
|
||||
size_t SkPath::readFromMemory(const void* storage, size_t length) {
|
||||
SkRBuffer buffer(storage, length);
|
||||
|
||||
|
@ -243,29 +243,42 @@ SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer) {
|
||||
unsigned rrectOrOvalStartIdx = (packed >> kRRectOrOvalStartIdx_SerializationShift) & 0x7;
|
||||
|
||||
int32_t verbCount, pointCount, conicCount;
|
||||
ptrdiff_t maxPtrDiff = std::numeric_limits<ptrdiff_t>::max();
|
||||
if (!buffer->readU32(&(ref->fGenerationID)) ||
|
||||
!buffer->readS32(&verbCount) ||
|
||||
verbCount < 0 ||
|
||||
static_cast<uint32_t>(verbCount) > maxPtrDiff/sizeof(uint8_t) ||
|
||||
!buffer->readS32(&pointCount) ||
|
||||
pointCount < 0 ||
|
||||
static_cast<uint32_t>(pointCount) > maxPtrDiff/sizeof(SkPoint) ||
|
||||
sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount >
|
||||
static_cast<size_t>(maxPtrDiff) ||
|
||||
!buffer->readS32(&conicCount) ||
|
||||
conicCount < 0) {
|
||||
!buffer->readS32(&verbCount) || (verbCount < 0) ||
|
||||
!buffer->readS32(&pointCount) || (pointCount < 0) ||
|
||||
!buffer->readS32(&conicCount) || (conicCount < 0))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint64_t pointSize64 = sk_64_mul(pointCount, sizeof(SkPoint));
|
||||
uint64_t conicSize64 = sk_64_mul(conicCount, sizeof(SkScalar));
|
||||
if (!SkTFitsIn<size_t>(pointSize64) || !SkTFitsIn<size_t>(conicSize64)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t verbSize = verbCount * sizeof(uint8_t);
|
||||
size_t pointSize = SkToSizeT(pointSize64);
|
||||
size_t conicSize = SkToSizeT(conicSize64);
|
||||
|
||||
{
|
||||
uint64_t requiredBufferSize = sizeof(SkRect);
|
||||
requiredBufferSize += verbSize;
|
||||
requiredBufferSize += pointSize;
|
||||
requiredBufferSize += conicSize;
|
||||
if (buffer->available() < requiredBufferSize) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ref->resetToSize(verbCount, pointCount, conicCount);
|
||||
SkASSERT(verbCount == ref->countVerbs());
|
||||
SkASSERT(verbCount == ref->countVerbs());
|
||||
SkASSERT(pointCount == ref->countPoints());
|
||||
SkASSERT(conicCount == ref->fConicWeights.count());
|
||||
|
||||
if (!buffer->read(ref->verbsMemWritable(), verbCount * sizeof(uint8_t)) ||
|
||||
!buffer->read(ref->fPoints, pointCount * sizeof(SkPoint)) ||
|
||||
!buffer->read(ref->fConicWeights.begin(), conicCount * sizeof(SkScalar)) ||
|
||||
if (!buffer->read(ref->verbsMemWritable(), verbSize) ||
|
||||
!buffer->read(ref->fPoints, pointSize) ||
|
||||
!buffer->read(ref->fConicWeights.begin(), conicSize) ||
|
||||
!buffer->read(&ref->fBounds, sizeof(SkRect))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -4775,3 +4775,58 @@ DEF_TEST(skbug_6450, r) {
|
||||
orr.setRectRadii(ro, rdo);
|
||||
SkMakeNullCanvas()->drawDRRect(orr, irr, SkPaint());
|
||||
}
|
||||
|
||||
DEF_TEST(PathRefSerialization, reporter) {
|
||||
SkPath path;
|
||||
const size_t numMoves = 5;
|
||||
const size_t numConics = 7;
|
||||
const size_t numPoints = numMoves + 2 * numConics;
|
||||
const size_t numVerbs = numMoves + numConics;
|
||||
for (size_t i = 0; i < numMoves; ++i) path.moveTo(1, 2);
|
||||
for (size_t i = 0; i < numConics; ++i) path.conicTo(1, 2, 3, 4, 5);
|
||||
REPORTER_ASSERT(reporter, path.countPoints() == numPoints);
|
||||
REPORTER_ASSERT(reporter, path.countVerbs() == numVerbs);
|
||||
|
||||
// Verify that path serializes/deserializes properly.
|
||||
sk_sp<SkData> data = path.serialize();
|
||||
size_t bytesWritten = data->size();
|
||||
|
||||
{
|
||||
SkPath readBack;
|
||||
REPORTER_ASSERT(reporter, readBack != path);
|
||||
size_t bytesRead = readBack.readFromMemory(data->data(), bytesWritten);
|
||||
REPORTER_ASSERT(reporter, bytesRead == bytesWritten);
|
||||
REPORTER_ASSERT(reporter, readBack == path);
|
||||
}
|
||||
|
||||
// uint32_t[] offset into serialized path.
|
||||
const size_t verbCountOffset = 4;
|
||||
const size_t pointCountOffset = 5;
|
||||
const size_t conicCountOffset = 6;
|
||||
|
||||
// Verify that this test is changing the right values.
|
||||
const int* writtenValues = static_cast<const int*>(data->data());
|
||||
REPORTER_ASSERT(reporter, writtenValues[verbCountOffset] == numVerbs);
|
||||
REPORTER_ASSERT(reporter, writtenValues[pointCountOffset] == numPoints);
|
||||
REPORTER_ASSERT(reporter, writtenValues[conicCountOffset] == numConics);
|
||||
|
||||
// Too many verbs, points, or conics fails to deserialize silently.
|
||||
const int tooManyObjects = INT_MAX;
|
||||
size_t offsets[] = {verbCountOffset, pointCountOffset, conicCountOffset};
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
SkAutoMalloc storage_copy(bytesWritten);
|
||||
memcpy(storage_copy.get(), data->data(), bytesWritten);
|
||||
static_cast<int*>(storage_copy.get())[offsets[i]] = tooManyObjects;
|
||||
SkPath readBack;
|
||||
size_t bytesRead = readBack.readFromMemory(storage_copy.get(), bytesWritten);
|
||||
REPORTER_ASSERT(reporter, !bytesRead);
|
||||
}
|
||||
|
||||
// One less byte (rounded down to alignment) than was written will also
|
||||
// fail to be deserialized.
|
||||
{
|
||||
SkPath readBack;
|
||||
size_t bytesRead = readBack.readFromMemory(data->data(), bytesWritten - 4);
|
||||
REPORTER_ASSERT(reporter, !bytesRead);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user