Checking structure sizes before reading them from memory to avoid overflowing the buffer's stream.

BUG=
R=reed@google.com

Review URL: https://codereview.chromium.org/41253002

git-svn-id: http://skia.googlecode.com/svn/trunk@12114 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index b6856d1..dad5ff9 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -559,9 +559,16 @@
         kMaxFlattenSize = 9 * sizeof(SkScalar) + sizeof(uint32_t)
     };
     // return the number of bytes written, whether or not buffer is null
-    uint32_t writeToMemory(void* buffer) const;
-    // return the number of bytes read
-    uint32_t readFromMemory(const void* buffer);
+    size_t writeToMemory(void* buffer) const;
+    /**
+     * Reads data from the buffer parameter
+     *
+     * @param buffer Memory to read from
+     * @param length Amount of memory available in the buffer
+     * @return number of bytes read (must be a multiple of 4) or
+     *         0 if there was not enough memory available
+     */
+    size_t readFromMemory(const void* buffer, size_t length);
 
     SkDEVCODE(void dump() const;)
     SkDEVCODE(void toString(SkString*) const;)
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 785eb77..2b111fe 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -899,16 +899,19 @@
     void dump() const;
 
     /**
-     *  Write the region to the buffer, and return the number of bytes written.
+     *  Write the path to the buffer, and return the number of bytes written.
      *  If buffer is NULL, it still returns the number of bytes.
      */
-    uint32_t writeToMemory(void* buffer) const;
-
+    size_t writeToMemory(void* buffer) const;
     /**
-     *  Initialized the region from the buffer, returning the number
-     *  of bytes actually read.
+     * Initializes the path from the buffer
+     *
+     * @param buffer Memory to read from
+     * @param length Amount of memory available in the buffer
+     * @return number of bytes read (must be a multiple of 4) or
+     *         0 if there was not enough memory available
      */
-    uint32_t readFromMemory(const void* buffer);
+    size_t readFromMemory(const void* buffer, size_t length);
 
     /** Returns a non-zero, globally unique value corresponding to the set of verbs
         and points in the path (but not the fill type [except on Android skbug.com/1762]).
diff --git a/include/core/SkRRect.h b/include/core/SkRRect.h
index 402e6c6..3c6386f 100644
--- a/include/core/SkRRect.h
+++ b/include/core/SkRRect.h
@@ -244,14 +244,20 @@
      *  write kSizeInMemory bytes, and that value is guaranteed to always be
      *  a multiple of 4. Return kSizeInMemory.
      */
-    uint32_t writeToMemory(void* buffer) const;
+    size_t writeToMemory(void* buffer) const;
 
     /**
-     *  Read the rrect from the specified buffer. This is guaranteed to always
-     *  read kSizeInMemory bytes, and that value is guaranteed to always be
-     *  a multiple of 4. Return kSizeInMemory.
+     * Reads the rrect from the specified buffer
+     *
+     * If the specified buffer is large enough, this will read kSizeInMemory bytes,
+     * and that value is guaranteed to always be a multiple of 4.
+     *
+     * @param buffer Memory to read from
+     * @param length Amount of memory available in the buffer
+     * @return number of bytes read (must be a multiple of 4) or
+     *         0 if there was not enough memory available
      */
-    uint32_t readFromMemory(const void* buffer);
+    size_t readFromMemory(const void* buffer, size_t length);
 
 private:
     SkRect fRect;
diff --git a/include/core/SkReader32.h b/include/core/SkReader32.h
index 7a8d22a..40ae12c 100644
--- a/include/core/SkReader32.h
+++ b/include/core/SkReader32.h
@@ -106,27 +106,20 @@
     int32_t readS32() { return this->readInt(); }
     uint32_t readU32() { return this->readInt(); }
 
-    void readPath(SkPath* path) {
-        size_t size = path->readFromMemory(this->peek());
-        SkASSERT(SkAlign4(size) == size);
-        (void)this->skip(size);
+    bool readPath(SkPath* path) {
+        return readObjectFromMemory(path);
     }
 
-    void readMatrix(SkMatrix* matrix) {
-        size_t size = matrix->readFromMemory(this->peek());
-        SkASSERT(SkAlign4(size) == size);
-        (void)this->skip(size);
+    bool readMatrix(SkMatrix* matrix) {
+        return readObjectFromMemory(matrix);
     }
 
-    SkRRect* readRRect(SkRRect* rrect) {
-        rrect->readFromMemory(this->skip(SkRRect::kSizeInMemory));
-        return rrect;
+    bool readRRect(SkRRect* rrect) {
+        return readObjectFromMemory(rrect);
     }
 
-    void readRegion(SkRegion* rgn) {
-        size_t size = rgn->readFromMemory(this->peek());
-        SkASSERT(SkAlign4(size) == size);
-        (void)this->skip(size);
+    bool readRegion(SkRegion* rgn) {
+        return readObjectFromMemory(rgn);
     }
 
     /**
@@ -143,6 +136,15 @@
     size_t readIntoString(SkString* copy);
 
 private:
+    template <typename T> bool readObjectFromMemory(T* obj) {
+        size_t size = obj->readFromMemory(this->peek(), this->available());
+        // If readFromMemory() fails (which means that available() was too small), it returns 0
+        bool success = (size > 0) && (size <= this->available()) && (SkAlign4(size) == size);
+        // In case of failure, we want to skip to the end
+        (void)this->skip(success ? size : this->available());
+        return success;
+    }
+
     // these are always 4-byte aligned
     const char* fCurr;  // current position within buffer
     const char* fStop;  // end of buffer
diff --git a/include/core/SkRegion.h b/include/core/SkRegion.h
index a088d54..c9aa8da 100644
--- a/include/core/SkRegion.h
+++ b/include/core/SkRegion.h
@@ -361,13 +361,16 @@
      *  Write the region to the buffer, and return the number of bytes written.
      *  If buffer is NULL, it still returns the number of bytes.
      */
-    uint32_t writeToMemory(void* buffer) const;
-
+    size_t writeToMemory(void* buffer) const;
     /**
-     *  Initialized the region from the buffer, returning the number
-     *  of bytes actually read.
+     * Initializes the region from the buffer
+     *
+     * @param buffer Memory to read from
+     * @param length Amount of memory available in the buffer
+     * @return number of bytes read (must be a multiple of 4) or
+     *         0 if there was not enough memory available
      */
-    uint32_t readFromMemory(const void* buffer);
+    size_t readFromMemory(const void* buffer, size_t length);
 
     /**
      *  Returns a reference to a global empty region. Just a convenience for
diff --git a/samplecode/SampleRegion.cpp b/samplecode/SampleRegion.cpp
index 1bd3162..39c27a8 100644
--- a/samplecode/SampleRegion.cpp
+++ b/samplecode/SampleRegion.cpp
@@ -280,7 +280,7 @@
                 SkASSERT(size == size2);
 
                 SkRegion    tmp3;
-                SkDEBUGCODE(size2 = ) tmp3.readFromMemory(buffer);
+                SkDEBUGCODE(size2 = ) tmp3.readFromMemory(buffer, 1000);
                 SkASSERT(size == size2);
 
                 SkASSERT(tmp3 == tmp);
diff --git a/src/core/SkBuffer.cpp b/src/core/SkBuffer.cpp
index 915264d..32a8011 100644
--- a/src/core/SkBuffer.cpp
+++ b/src/core/SkBuffer.cpp
@@ -34,6 +34,13 @@
     return n;
 }
 
+void SkRBufferWithSizeCheck::read(void* buffer, size_t size) {
+    fError = fError || (fPos + size > fStop);
+    if (!fError && (size > 0)) {
+        readNoSizeCheck(buffer, size);
+    }
+}
+
 void* SkWBuffer::skip(size_t size)
 {
     void* result = fPos;
diff --git a/src/core/SkBuffer.h b/src/core/SkBuffer.h
index 9633389..1a4c6c2 100644
--- a/src/core/SkBuffer.h
+++ b/src/core/SkBuffer.h
@@ -56,7 +56,7 @@
     /** Read the specified number of bytes from the data pointer. If buffer is not
         null, copy those bytes into buffer.
     */
-    void read(void* buffer, size_t size) {
+    virtual void read(void* buffer, size_t size) {
         if (size) {
             this->readNoSizeCheck(buffer, size);
         }
@@ -74,7 +74,7 @@
     uint8_t     readU8() { uint8_t x; read(&x, 1); return x; }
     bool        readBool() { return this->readU8() != 0; }
 
-private:
+protected:
     void    readNoSizeCheck(void* buffer, size_t size);
 
     const char* fData;
@@ -82,6 +82,28 @@
     const char* fStop;
 };
 
+/** \class SkRBufferWithSizeCheck
+
+    Same as SkRBuffer, except that a size check is performed before the read operation and an
+    error is set if the read operation is attempting to read past the end of the data.
+*/
+class SkRBufferWithSizeCheck : public SkRBuffer {
+public:
+    SkRBufferWithSizeCheck(const void* data, size_t size) : SkRBuffer(data, size), fError(false) {}
+
+    /** Read the specified number of bytes from the data pointer. If buffer is not
+        null and the number of bytes to read does not overflow this object's data,
+        copy those bytes into buffer.
+    */
+    virtual void read(void* buffer, size_t size) SK_OVERRIDE;
+
+    /** Returns whether or not a read operation attempted to read past the end of the data.
+    */
+    bool isValid() const { return !fError; }
+private:
+    bool fError;
+};
+
 /** \class SkWBuffer
 
     Light weight class for writing data to a memory block.
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
index 5bcb35b..cd7bcea 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -1921,20 +1921,25 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-uint32_t SkMatrix::writeToMemory(void* buffer) const {
+size_t SkMatrix::writeToMemory(void* buffer) const {
     // TODO write less for simple matrices
+    static const size_t sizeInMemory = 9 * sizeof(SkScalar);
     if (buffer) {
-        memcpy(buffer, fMat, 9 * sizeof(SkScalar));
+        memcpy(buffer, fMat, sizeInMemory);
     }
-    return 9 * sizeof(SkScalar);
+    return sizeInMemory;
 }
 
-uint32_t SkMatrix::readFromMemory(const void* buffer) {
+size_t SkMatrix::readFromMemory(const void* buffer, size_t length) {
+    static const size_t sizeInMemory = 9 * sizeof(SkScalar);
+    if (length < sizeInMemory) {
+        return 0;
+    }
     if (buffer) {
-        memcpy(fMat, buffer, 9 * sizeof(SkScalar));
+        memcpy(fMat, buffer, sizeInMemory);
         this->setTypeMask(kUnknown_Mask);
     }
-    return 9 * sizeof(SkScalar);
+    return sizeInMemory;
 }
 
 #ifdef SK_DEVELOPER
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 60cfe03..982d6ad 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -2066,7 +2066,7 @@
     Format in compressed buffer: [ptCount, verbCount, pts[], verbs[]]
 */
 
-uint32_t SkPath::writeToMemory(void* storage) const {
+size_t SkPath::writeToMemory(void* storage) const {
     SkDEBUGCODE(this->validate();)
 
     if (NULL == storage) {
@@ -2090,11 +2090,11 @@
     fPathRef->writeToBuffer(&buffer);
 
     buffer.padToAlign4();
-    return SkToU32(buffer.pos());
+    return buffer.pos();
 }
 
-uint32_t SkPath::readFromMemory(const void* storage) {
-    SkRBuffer   buffer(storage);
+size_t SkPath::readFromMemory(const void* storage, size_t length) {
+    SkRBufferWithSizeCheck buffer(storage, length);
 
     uint32_t packed = buffer.readS32();
     fIsOval = (packed >> kIsOval_SerializationShift) & 1;
@@ -2108,14 +2108,14 @@
 
     fPathRef.reset(SkPathRef::CreateFromBuffer(&buffer
 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TOO
-        , newFormat, packed)
+        , newFormat, packed
 #endif
-        );
+        ));
 
     buffer.skipToAlign4();
 
     SkDEBUGCODE(this->validate();)
-    return SkToU32(buffer.pos());
+    return buffer.isValid() ? buffer.pos() : 0;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index f2d959d..5a016d4 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -997,7 +997,8 @@
             case DRAW_RRECT: {
                 const SkPaint& paint = *getPaint(reader);
                 SkRRect rrect;
-                canvas.drawRRect(*reader.readRRect(&rrect), paint);
+                reader.readRRect(&rrect);
+                canvas.drawRRect(rrect, paint);
             } break;
             case DRAW_SPRITE: {
                 const SkPaint* paint = getPaint(reader);
diff --git a/src/core/SkRRect.cpp b/src/core/SkRRect.cpp
index e3d11cb..bcbf37e 100644
--- a/src/core/SkRRect.cpp
+++ b/src/core/SkRRect.cpp
@@ -259,7 +259,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-uint32_t SkRRect::writeToMemory(void* buffer) const {
+size_t SkRRect::writeToMemory(void* buffer) const {
     SkASSERT(kSizeInMemory == sizeof(SkRect) + sizeof(fRadii));
 
     memcpy(buffer, &fRect, sizeof(SkRect));
@@ -267,7 +267,11 @@
     return kSizeInMemory;
 }
 
-uint32_t SkRRect::readFromMemory(const void* buffer) {
+size_t SkRRect::readFromMemory(const void* buffer, size_t length) {
+    if (length < kSizeInMemory) {
+        return 0;
+    }
+
     SkScalar storage[12];
     SkASSERT(sizeof(storage) == kSizeInMemory);
 
diff --git a/src/core/SkRegion.cpp b/src/core/SkRegion.cpp
index 02994bf..468be67 100644
--- a/src/core/SkRegion.cpp
+++ b/src/core/SkRegion.cpp
@@ -1100,9 +1100,9 @@
 
 #include "SkBuffer.h"
 
-uint32_t SkRegion::writeToMemory(void* storage) const {
+size_t SkRegion::writeToMemory(void* storage) const {
     if (NULL == storage) {
-        uint32_t size = sizeof(int32_t); // -1 (empty), 0 (rect), runCount
+        size_t size = sizeof(int32_t); // -1 (empty), 0 (rect), runCount
         if (!this->isEmpty()) {
             size += sizeof(fBounds);
             if (this->isComplex()) {
@@ -1133,11 +1133,11 @@
     return buffer.pos();
 }
 
-uint32_t SkRegion::readFromMemory(const void* storage) {
-    SkRBuffer   buffer(storage);
-    SkRegion    tmp;
-    int32_t     count;
-
+size_t SkRegion::readFromMemory(const void* storage, size_t length) {
+    SkRBufferWithSizeCheck  buffer(storage, length);
+    SkRegion                tmp;
+    int32_t                 count;
+    
     count = buffer.readS32();
     if (count >= 0) {
         buffer.read(&tmp.fBounds, sizeof(tmp.fBounds));
@@ -1150,8 +1150,12 @@
             buffer.read(tmp.fRunHead->writable_runs(), count * sizeof(RunType));
         }
     }
-    this->swap(tmp);
-    return buffer.pos();
+    size_t sizeRead = 0;
+    if (buffer.isValid()) {
+        this->swap(tmp);
+        sizeRead = buffer.pos();
+    }
+    return sizeRead;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkValidatingReadBuffer.cpp b/src/core/SkValidatingReadBuffer.cpp
index 9f094f9..384dd10 100644
--- a/src/core/SkValidatingReadBuffer.cpp
+++ b/src/core/SkValidatingReadBuffer.cpp
@@ -118,8 +118,8 @@
 }
 
 void SkValidatingReadBuffer::readMatrix(SkMatrix* matrix) {
-    const size_t size = matrix->readFromMemory(fReader.peek());
-    this->validate(SkAlign4(size) == size);
+    const size_t size = matrix->readFromMemory(fReader.peek(), fReader.available());
+    this->validate((SkAlign4(size) != size) || (0 == size));
     if (!fError) {
         (void)this->skip(size);
     }
@@ -140,16 +140,16 @@
 }
 
 void SkValidatingReadBuffer::readRegion(SkRegion* region) {
-    const size_t size = region->readFromMemory(fReader.peek());
-    this->validate(SkAlign4(size) == size);
+    const size_t size = region->readFromMemory(fReader.peek(), fReader.available());
+    this->validate((SkAlign4(size) != size) || (0 == size));
     if (!fError) {
         (void)this->skip(size);
     }
 }
 
 void SkValidatingReadBuffer::readPath(SkPath* path) {
-    const size_t size = path->readFromMemory(fReader.peek());
-    this->validate(SkAlign4(size) == size);
+    const size_t size = path->readFromMemory(fReader.peek(), fReader.available());
+    this->validate((SkAlign4(size) != size) || (0 == size));
     if (!fError) {
         (void)this->skip(size);
     }
@@ -189,6 +189,8 @@
 }
 
 uint32_t SkValidatingReadBuffer::getArrayCount() {
+    const size_t inc = sizeof(uint32_t);
+    fError = fError || !IsPtrAlign4(fReader.peek()) || !fReader.isAvailable(inc);
     return *(uint32_t*)fReader.peek();
 }
 
diff --git a/tests/MatrixTest.cpp b/tests/MatrixTest.cpp
index 07eacb6..e988694 100644
--- a/tests/MatrixTest.cpp
+++ b/tests/MatrixTest.cpp
@@ -112,18 +112,19 @@
 
 static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) {
     // add 100 in case we have a bug, I don't want to kill my stack in the test
-    char buffer[SkMatrix::kMaxFlattenSize + 100];
-    uint32_t size1 = m.writeToMemory(NULL);
-    uint32_t size2 = m.writeToMemory(buffer);
+    static const size_t kBufferSize = SkMatrix::kMaxFlattenSize + 100;
+    char buffer[kBufferSize];
+    size_t size1 = m.writeToMemory(NULL);
+    size_t size2 = m.writeToMemory(buffer);
     REPORTER_ASSERT(reporter, size1 == size2);
     REPORTER_ASSERT(reporter, size1 <= SkMatrix::kMaxFlattenSize);
 
     SkMatrix m2;
-    uint32_t size3 = m2.readFromMemory(buffer);
+    size_t size3 = m2.readFromMemory(buffer, kBufferSize);
     REPORTER_ASSERT(reporter, size1 == size3);
     REPORTER_ASSERT(reporter, are_equal(reporter, m, m2));
 
-    char buffer2[SkMatrix::kMaxFlattenSize + 100];
+    char buffer2[kBufferSize];
     size3 = m2.writeToMemory(buffer2);
     REPORTER_ASSERT(reporter, size1 == size3);
     REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index d879ea6..f6c2a7a 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -1808,12 +1808,12 @@
     // create a buffer that should be much larger than the path so we don't
     // kill our stack if writer goes too far.
     char buffer[1024];
-    uint32_t size1 = p.writeToMemory(NULL);
-    uint32_t size2 = p.writeToMemory(buffer);
+    size_t size1 = p.writeToMemory(NULL);
+    size_t size2 = p.writeToMemory(buffer);
     REPORTER_ASSERT(reporter, size1 == size2);
 
     SkPath p2;
-    uint32_t size3 = p2.readFromMemory(buffer);
+    size_t size3 = p2.readFromMemory(buffer, 1024);
     REPORTER_ASSERT(reporter, size1 == size3);
     REPORTER_ASSERT(reporter, p == p2);
 
diff --git a/tests/SerializationTest.cpp b/tests/SerializationTest.cpp
index d2c6925..88b9fbb 100644
--- a/tests/SerializationTest.cpp
+++ b/tests/SerializationTest.cpp
@@ -9,140 +9,201 @@
 #include "SkValidatingReadBuffer.h"
 #include "Test.h"
 
-static void Tests(skiatest::Reporter* reporter) {
-    {
-        static const uint32_t arraySize = 512;
-        unsigned char data[arraySize] = {0};
-        SkOrderedWriteBuffer writer(1024);
-        writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
+static const uint32_t kArraySize = 64;
+
+template<typename T>
+static void TestAlignment(T* testObj, skiatest::Reporter* reporter) {
+    // Test memory read/write functions directly
+    unsigned char dataWritten[1024];
+    size_t bytesWrittenToMemory = testObj->writeToMemory(dataWritten);
+    REPORTER_ASSERT(reporter, SkAlign4(bytesWrittenToMemory) == bytesWrittenToMemory);
+    size_t bytesReadFromMemory = testObj->readFromMemory(dataWritten, bytesWrittenToMemory);
+    REPORTER_ASSERT(reporter, SkAlign4(bytesReadFromMemory) == bytesReadFromMemory);
+}
+
+template<typename T> struct SerializationUtils {
+};
+
+template<> struct SerializationUtils<SkMatrix> {
+    static void Write(SkOrderedWriteBuffer& writer, const SkMatrix* matrix) {
+        writer.writeMatrix(*matrix);
+    }
+    static void Read(SkValidatingReadBuffer& reader, SkMatrix* matrix) {
+        reader.readMatrix(matrix);
+    }
+};
+
+template<> struct SerializationUtils<SkPath> {
+    static void Write(SkOrderedWriteBuffer& writer, const SkPath* path) {
+        writer.writePath(*path);
+    }
+    static void Read(SkValidatingReadBuffer& reader, SkPath* path) {
+        reader.readPath(path);
+    }
+};
+
+template<> struct SerializationUtils<SkRegion> {
+    static void Write(SkOrderedWriteBuffer& writer, const SkRegion* region) {
+        writer.writeRegion(*region);
+    }
+    static void Read(SkValidatingReadBuffer& reader, SkRegion* region) {
+        reader.readRegion(region);
+    }
+};
+
+template<> struct SerializationUtils<unsigned char> {
+    static void Write(SkOrderedWriteBuffer& writer, unsigned char* data, uint32_t arraySize) {
         writer.writeByteArray(data, arraySize);
-        uint32_t bytesWritten = writer.bytesWritten();
-        // This should write the length (in 4 bytes) and the array
-        REPORTER_ASSERT(reporter, (4 + arraySize) == bytesWritten);
-
-        unsigned char dataWritten[1024];
-        writer.writeToMemory(dataWritten);
-
-        // Make sure this fails when it should
-        SkValidatingReadBuffer buffer(dataWritten, bytesWritten);
-        unsigned char dataRead[arraySize];
-        bool success = buffer.readByteArray(dataRead, 256);
-        // This should have failed, since 256 < sizeInBytes
-        REPORTER_ASSERT(reporter, !success);
-
-        // Make sure this succeeds when it should
-        SkValidatingReadBuffer buffer2(dataWritten, bytesWritten);
-        success = buffer2.readByteArray(dataRead, arraySize);
-        // This should have succeeded, since there are enough bytes to read this
-        REPORTER_ASSERT(reporter, success);
     }
+    static bool Read(SkValidatingReadBuffer& reader, unsigned char* data, uint32_t arraySize) {
+        return reader.readByteArray(data, arraySize);
+    }
+};
 
-    {
-        static const uint32_t arraySize = 64;
-        SkColor data[arraySize];
-        SkOrderedWriteBuffer writer(1024);
-        writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
+template<> struct SerializationUtils<SkColor> {
+    static void Write(SkOrderedWriteBuffer& writer, SkColor* data, uint32_t arraySize) {
         writer.writeColorArray(data, arraySize);
-        uint32_t bytesWritten = writer.bytesWritten();
-        // This should write the length (in 4 bytes) and the array
-        REPORTER_ASSERT(reporter, (4 + arraySize * sizeof(SkColor)) == bytesWritten);
-
-        unsigned char dataWritten[1024];
-        writer.writeToMemory(dataWritten);
-
-        // Make sure this fails when it should
-        SkValidatingReadBuffer buffer(dataWritten, bytesWritten);
-        SkColor dataRead[arraySize];
-        bool success = buffer.readColorArray(dataRead, 32);
-        // This should have failed, since 256 < sizeInBytes
-        REPORTER_ASSERT(reporter, !success);
-
-        // Make sure this succeeds when it should
-        SkValidatingReadBuffer buffer2(dataWritten, bytesWritten);
-        success = buffer2.readColorArray(dataRead, arraySize);
-        // This should have succeeded, since there are enough bytes to read this
-        REPORTER_ASSERT(reporter, success);
     }
+    static bool Read(SkValidatingReadBuffer& reader, SkColor* data, uint32_t arraySize) {
+        return reader.readColorArray(data, arraySize);
+    }
+};
 
-    {
-        static const uint32_t arraySize = 64;
-        int32_t data[arraySize];
-        SkOrderedWriteBuffer writer(1024);
-        writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
+template<> struct SerializationUtils<int32_t> {
+    static void Write(SkOrderedWriteBuffer& writer, int32_t* data, uint32_t arraySize) {
         writer.writeIntArray(data, arraySize);
-        uint32_t bytesWritten = writer.bytesWritten();
-        // This should write the length (in 4 bytes) and the array
-        REPORTER_ASSERT(reporter, (4 + arraySize * sizeof(int32_t)) == bytesWritten);
-
-        unsigned char dataWritten[1024];
-        writer.writeToMemory(dataWritten);
-
-        // Make sure this fails when it should
-        SkValidatingReadBuffer buffer(dataWritten, bytesWritten);
-        int32_t dataRead[arraySize];
-        bool success = buffer.readIntArray(dataRead, 32);
-        // This should have failed, since 256 < sizeInBytes
-        REPORTER_ASSERT(reporter, !success);
-
-        // Make sure this succeeds when it should
-        SkValidatingReadBuffer buffer2(dataWritten, bytesWritten);
-        success = buffer2.readIntArray(dataRead, arraySize);
-        // This should have succeeded, since there are enough bytes to read this
-        REPORTER_ASSERT(reporter, success);
     }
+    static bool Read(SkValidatingReadBuffer& reader, int32_t* data, uint32_t arraySize) {
+        return reader.readIntArray(data, arraySize);
+    }
+};
 
-    {
-        static const uint32_t arraySize = 64;
-        SkPoint data[arraySize];
-        SkOrderedWriteBuffer writer(1024);
-        writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
+template<> struct SerializationUtils<SkPoint> {
+    static void Write(SkOrderedWriteBuffer& writer, SkPoint* data, uint32_t arraySize) {
         writer.writePointArray(data, arraySize);
-        uint32_t bytesWritten = writer.bytesWritten();
-        // This should write the length (in 4 bytes) and the array
-        REPORTER_ASSERT(reporter, (4 + arraySize * sizeof(SkPoint)) == bytesWritten);
+    }
+    static bool Read(SkValidatingReadBuffer& reader, SkPoint* data, uint32_t arraySize) {
+        return reader.readPointArray(data, arraySize);
+    }
+};
 
-        unsigned char dataWritten[1024];
-        writer.writeToMemory(dataWritten);
+template<> struct SerializationUtils<SkScalar> {
+    static void Write(SkOrderedWriteBuffer& writer, SkScalar* data, uint32_t arraySize) {
+        writer.writeScalarArray(data, arraySize);
+    }
+    static bool Read(SkValidatingReadBuffer& reader, SkScalar* data, uint32_t arraySize) {
+        return reader.readScalarArray(data, arraySize);
+    }
+};
 
-        // Make sure this fails when it should
-        SkValidatingReadBuffer buffer(dataWritten, bytesWritten);
-        SkPoint dataRead[arraySize];
-        bool success = buffer.readPointArray(dataRead, 32);
-        // This should have failed, since 256 < sizeInBytes
-        REPORTER_ASSERT(reporter, !success);
+template<typename T>
+static void TestObjectSerialization(T* testObj, skiatest::Reporter* reporter) {
+    SkOrderedWriteBuffer writer(1024);
+    writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
+    SerializationUtils<T>::Write(writer, testObj);
+    size_t bytesWritten = writer.bytesWritten();
 
-        // Make sure this succeeds when it should
-        SkValidatingReadBuffer buffer2(dataWritten, bytesWritten);
-        success = buffer2.readPointArray(dataRead, arraySize);
-        // This should have succeeded, since there are enough bytes to read this
-        REPORTER_ASSERT(reporter, success);
+    unsigned char dataWritten[1024];
+    writer.writeToMemory(dataWritten);
+
+    // Make sure this fails when it should
+    SkValidatingReadBuffer buffer(dataWritten, bytesWritten - 1);
+    const unsigned char* peekBefore = static_cast<const unsigned char*>(buffer.skip(0));
+    SerializationUtils<T>::Read(buffer, testObj);
+    const unsigned char* peekAfter = static_cast<const unsigned char*>(buffer.skip(0));
+    // This should have failed, since the buffer is too small to read a matrix from it
+    REPORTER_ASSERT(reporter, peekBefore == peekAfter);
+
+    // Make sure this succeeds when it should
+    SkValidatingReadBuffer buffer2(dataWritten, bytesWritten);
+    peekBefore = static_cast<const unsigned char*>(buffer2.skip(0));
+    SerializationUtils<T>::Read(buffer2, testObj);
+    peekAfter = static_cast<const unsigned char*>(buffer2.skip(0));
+    // This should have succeeded, since there are enough bytes to read this
+    REPORTER_ASSERT(reporter, static_cast<size_t>(peekAfter - peekBefore) == bytesWritten);
+
+    TestAlignment(testObj, reporter);
+}
+
+template<typename T>
+static void TestArraySerialization(T* data, skiatest::Reporter* reporter) {
+    SkOrderedWriteBuffer writer(1024);
+    writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
+    SerializationUtils<T>::Write(writer, data, kArraySize);
+    size_t bytesWritten = writer.bytesWritten();
+    // This should write the length (in 4 bytes) and the array
+    REPORTER_ASSERT(reporter, (4 + kArraySize * sizeof(T)) == bytesWritten);
+
+    unsigned char dataWritten[1024];
+    writer.writeToMemory(dataWritten);
+
+    // Make sure this fails when it should
+    SkValidatingReadBuffer buffer(dataWritten, bytesWritten);
+    T dataRead[kArraySize];
+    bool success = SerializationUtils<T>::Read(buffer, dataRead, kArraySize / 2);
+    // This should have failed, since the provided size was too small
+    REPORTER_ASSERT(reporter, !success);
+
+    // Make sure this succeeds when it should
+    SkValidatingReadBuffer buffer2(dataWritten, bytesWritten);
+    success = SerializationUtils<T>::Read(buffer2, dataRead, kArraySize);
+    // This should have succeeded, since there are enough bytes to read this
+    REPORTER_ASSERT(reporter, success);
+}
+
+static void Tests(skiatest::Reporter* reporter) {
+    // Test matrix serialization
+    {
+        SkMatrix matrix = SkMatrix::I();
+        TestObjectSerialization(&matrix, reporter);
+     }
+
+    // Test path serialization
+    {
+        SkPath path;
+        TestObjectSerialization(&path, reporter);
     }
 
+    // Test region serialization
     {
-        static const uint32_t arraySize = 64;
-        SkScalar data[arraySize];
-        SkOrderedWriteBuffer writer(1024);
-        writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
-        writer.writeScalarArray(data, arraySize);
-        uint32_t bytesWritten = writer.bytesWritten();
-        // This should write the length (in 4 bytes) and the array
-        REPORTER_ASSERT(reporter, (4 + arraySize * sizeof(SkScalar)) == bytesWritten);
+        SkRegion region;
+        TestObjectSerialization(&region, reporter);
+    }
 
-        unsigned char dataWritten[1024];
-        writer.writeToMemory(dataWritten);
+    // Test rrect serialization
+    {
+        SkRRect rrect;
+        TestAlignment(&rrect, reporter);
+    }
 
-        // Make sure this fails when it should
-        SkValidatingReadBuffer buffer(dataWritten, bytesWritten);
-        SkScalar dataRead[arraySize];
-        bool success = buffer.readScalarArray(dataRead, 32);
-        // This should have failed, since 256 < sizeInBytes
-        REPORTER_ASSERT(reporter, !success);
+    // Test readByteArray
+    {
+        unsigned char data[kArraySize] = {0};
+        TestArraySerialization(data, reporter);
+    }
 
-        // Make sure this succeeds when it should
-        SkValidatingReadBuffer buffer2(dataWritten, bytesWritten);
-        success = buffer2.readScalarArray(dataRead, arraySize);
-        // This should have succeeded, since there are enough bytes to read this
-        REPORTER_ASSERT(reporter, success);
+    // Test readColorArray
+    {
+        SkColor data[kArraySize];
+        TestArraySerialization(data, reporter);
+    }
+
+    // Test readIntArray
+    {
+        int32_t data[kArraySize];
+        TestArraySerialization(data, reporter);
+    }
+
+    // Test readPointArray
+    {
+        SkPoint data[kArraySize];
+        TestArraySerialization(data, reporter);
+    }
+
+    // Test readScalarArray
+    {
+        SkScalar data[kArraySize];
+        TestArraySerialization(data, reporter);
     }
 }