From ada77e595653dc37e2f4fa9f1324b9c9b32417c0 Mon Sep 17 00:00:00 2001 From: Pale Moon Date: Tue, 15 May 2018 17:47:26 +0200 Subject: Add and use Skia's "Safe Math" (DiD) --- gfx/skia/trunk/include/core/SkTDArray.h | 4 +- gfx/skia/trunk/include/core/SkTypes.h | 8 +- gfx/skia/trunk/src/core/SkMallocPixelRef.cpp | 13 ++++ gfx/skia/trunk/src/core/SkMath.cpp | 15 ++++ gfx/skia/trunk/src/core/SkSafeMath.h | 107 +++++++++++++++++++++++++++ 5 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 gfx/skia/trunk/src/core/SkSafeMath.h diff --git a/gfx/skia/trunk/include/core/SkTDArray.h b/gfx/skia/trunk/include/core/SkTDArray.h index 4c90460b9..4c2094830 100644 --- a/gfx/skia/trunk/include/core/SkTDArray.h +++ b/gfx/skia/trunk/include/core/SkTDArray.h @@ -30,7 +30,7 @@ public: fData = NULL; #endif if (count) { - fArray = (T*)sk_malloc_throw(count * sizeof(T)); + fArray = (T*)sk_malloc_throw(count, sizeof(T)); #ifdef SK_DEBUG fData = (ArrayT*)fArray; #endif @@ -379,7 +379,7 @@ private: SkASSERT(count > fReserve); fReserve = count + 4; fReserve += fReserve / 4; - fArray = (T*)sk_realloc_throw(fArray, fReserve * sizeof(T)); + fArray = (T*)sk_realloc_throw(fArray, fReserve, sizeof(T)); #ifdef SK_DEBUG fData = (ArrayT*)fArray; #endif diff --git a/gfx/skia/trunk/include/core/SkTypes.h b/gfx/skia/trunk/include/core/SkTypes.h index 4d590c24d..e94ca753c 100644 --- a/gfx/skia/trunk/include/core/SkTypes.h +++ b/gfx/skia/trunk/include/core/SkTypes.h @@ -65,6 +65,11 @@ SK_API extern void* sk_calloc(size_t size); */ SK_API extern void* sk_calloc_throw(size_t size); +// Performs a safe multiply count * elemSize, checking for overflow +SK_API extern void* sk_calloc_throw(size_t count, size_t elemSize); +SK_API extern void* sk_malloc_throw(size_t count, size_t elemSize); +SK_API extern void* sk_realloc_throw(void* buffer, size_t count, size_t elemSize); + // bzero is safer than memset, but we can't rely on it, so... sk_bzero() static inline void sk_bzero(void* buffer, size_t size) { memset(buffer, 0, size); @@ -268,7 +273,8 @@ typedef uint8_t SkBool8; #define SK_MinS32 -SK_MaxS32 #define SK_MaxU32 0xFFFFFFFF #define SK_MinU32 0 -#define SK_NaN32 (1 << 31) +#define SK_NaN32 ((int) (1 << 31)) +#define SK_MaxSizeT SIZE_MAX /** Returns true if the value can be represented with signed 16bits */ diff --git a/gfx/skia/trunk/src/core/SkMallocPixelRef.cpp b/gfx/skia/trunk/src/core/SkMallocPixelRef.cpp index 0d5016483..10d8bc5f2 100644 --- a/gfx/skia/trunk/src/core/SkMallocPixelRef.cpp +++ b/gfx/skia/trunk/src/core/SkMallocPixelRef.cpp @@ -8,8 +8,21 @@ #include "SkMallocPixelRef.h" #include "SkBitmap.h" #include "SkReadBuffer.h" +#include "SkSafeMath.h" #include "SkWriteBuffer.h" +void* sk_calloc_throw(size_t count, size_t elemSize) { + return sk_calloc_throw(SkSafeMath::Mul(count, elemSize)); +} + +void* sk_malloc_throw(size_t count, size_t elemSize) { + return sk_malloc_throw(SkSafeMath::Mul(count, elemSize)); +} + +void* sk_realloc_throw(void* buffer, size_t count, size_t elemSize) { + return sk_realloc_throw(buffer, SkSafeMath::Mul(count, elemSize)); +} + // assumes ptr was allocated via sk_malloc static void sk_free_releaseproc(void* ptr, void*) { sk_free(ptr); diff --git a/gfx/skia/trunk/src/core/SkMath.cpp b/gfx/skia/trunk/src/core/SkMath.cpp index aeacebecd..f494ced79 100644 --- a/gfx/skia/trunk/src/core/SkMath.cpp +++ b/gfx/skia/trunk/src/core/SkMath.cpp @@ -6,6 +6,7 @@ */ #include "SkMathPriv.h" +#include "SkSafeMath.h" #include "SkFloatBits.h" #include "SkFloatingPoint.h" #include "SkScalar.h" @@ -287,3 +288,17 @@ SkFixed SkFixedSinCos(SkFixed radians, SkFixed* cosValuePtr) { } return sinValue; } + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +size_t SkSafeMath::Add(size_t x, size_t y) { + SkSafeMath tmp; + size_t sum = tmp.add(x, y); + return tmp.ok() ? sum : SK_MaxSizeT; +} + +size_t SkSafeMath::Mul(size_t x, size_t y) { + SkSafeMath tmp; + size_t prod = tmp.mul(x, y); + return tmp.ok() ? prod : SK_MaxSizeT; +} diff --git a/gfx/skia/trunk/src/core/SkSafeMath.h b/gfx/skia/trunk/src/core/SkSafeMath.h new file mode 100644 index 000000000..b8e913956 --- /dev/null +++ b/gfx/skia/trunk/src/core/SkSafeMath.h @@ -0,0 +1,107 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSafeMath_DEFINED +#define SkSafeMath_DEFINED + +#include "SkTypes.h" +#include + +// SkSafeMath always check that a series of operations do not overflow. +// This must be correct for all platforms, because this is a check for safety at runtime. + +class SkSafeMath { +public: + SkSafeMath() = default; + + bool ok() const { return fOK; } + explicit operator bool() const { return fOK; } + + size_t mul(size_t x, size_t y) { + return sizeof(size_t) == sizeof(uint64_t) ? mul64(x, y) : mul32(x, y); + } + + size_t add(size_t x, size_t y) { + size_t result = x + y; + fOK &= result >= x; + return result; + } + + /** + * Return a + b, unless this result is an overflow/underflow. In those cases, fOK will + * be set to false, and it is undefined what this returns. + */ + int addInt(int a, int b) { + if (b < 0 && a < std::numeric_limits::min() - b) { + fOK = false; + return a; + } else if (b > 0 && a > std::numeric_limits::max() - b) { + fOK = false; + return a; + } + return a + b; + } + + size_t alignUp(size_t x, size_t alignment) { + SkASSERT(alignment && !(alignment & (alignment - 1))); + return add(x, alignment - 1) & ~(alignment - 1); + } + + template T castTo(size_t value) { + if (!SkTFitsIn(value)) { + fOK = false; + } + return static_cast(value); + } + + // These saturate to their results + static size_t Add(size_t x, size_t y); + static size_t Mul(size_t x, size_t y); + static size_t Align4(size_t x) { + SkSafeMath safe; + return safe.alignUp(x, 4); + } + +private: + uint32_t mul32(uint32_t x, uint32_t y) { + uint64_t bx = x; + uint64_t by = y; + uint64_t result = bx * by; + fOK &= result >> 32 == 0; + return result; + } + + uint64_t mul64(uint64_t x, uint64_t y) { + if (x <= std::numeric_limits::max() >> 32 + && y <= std::numeric_limits::max() >> 32) { + return x * y; + } else { + auto hi = [](uint64_t x) { return x >> 32; }; + auto lo = [](uint64_t x) { return x & 0xFFFFFFFF; }; + + uint64_t lx_ly = lo(x) * lo(y); + uint64_t hx_ly = hi(x) * lo(y); + uint64_t lx_hy = lo(x) * hi(y); + uint64_t hx_hy = hi(x) * hi(y); + uint64_t result = 0; + result = this->add(lx_ly, (hx_ly << 32)); + result = this->add(result, (lx_hy << 32)); + fOK &= (hx_hy + (hx_ly >> 32) + (lx_hy >> 32)) == 0; + + #if defined(SK_DEBUG) && defined(__clang__) && defined(__x86_64__) + auto double_check = (unsigned __int128)x * y; + SkASSERT(result == (double_check & 0xFFFFFFFFFFFFFFFF)); + SkASSERT(!fOK || (double_check >> 64 == 0)); + #endif + + return result; + } + } + bool fOK = true; +}; + +#endif//SkSafeMath_DEFINED \ No newline at end of file -- cgit v1.2.3