diff --git a/src/serialize.h b/src/serialize.h index 9922532849dfb..b14ac89c8ff2a 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -33,7 +33,11 @@ class CScript; -static const unsigned int MAX_SIZE = 0x02000000; +/** + * The maximum size of a serialized object in bytes or number of elements + * (for eg vectors) when the size is encoded as CompactSize. + */ +static constexpr uint64_t MAX_SIZE = 0x02000000; /** Maximum amount of memory (in bytes) to allocate at once when deserializing vectors. */ static const unsigned int MAX_VECTOR_ALLOCATE = 5000000; @@ -334,8 +338,14 @@ void WriteCompactSize(Stream& os, uint64_t nSize) return; } -template -uint64_t ReadCompactSize(Stream& is) +/** + * Decode a CompactSize-encoded variable-length integer. + * + * As these are primarily used to encode the size of vector-like serializations, by default a range + * check is performed. When used as a generic number encoding, range_check should be set to false. + */ +template +uint64_t ReadCompactSize(Stream& is, bool range_check = true) { uint8_t chSize = ser_readdata8(is); uint64_t nSizeRet = 0; @@ -354,8 +364,9 @@ uint64_t ReadCompactSize(Stream& is) if (nSizeRet < 0x100000000ULL) throw std::ios_base::failure("non-canonical ReadCompactSize()"); } - if (nSizeRet > (uint64_t)MAX_SIZE) - throw std::ios_base::failure("ReadCompactSize() : size too large"); + if (range_check && nSizeRet > MAX_SIZE) { + throw std::ios_base::failure("ReadCompactSize(): size too large"); + } return nSizeRet; } @@ -489,7 +500,7 @@ static inline Wrapper Using(T&& t) { return Wrapper>(obj) #define VARINT(obj) Using>(obj) -#define COMPACTSIZE(obj) Using(obj) +#define COMPACTSIZE(obj) Using>(obj) #define LIMITED_STRING(obj,n) Using>(obj) /** Serialization wrapper class for integers in VarInt format. */ @@ -552,12 +563,13 @@ struct CustomUintFormatter template using BigEndianFormatter = CustomUintFormatter; /** Formatter for integers in CompactSize format. */ +template struct CompactSizeFormatter { template void Unser(Stream& s, I& v) { - uint64_t n = ReadCompactSize(s); + uint64_t n = ReadCompactSize(s, RangeCheck); if (n < std::numeric_limits::min() || n > std::numeric_limits::max()) { throw std::ios_base::failure("CompactSize exceeds limit of type"); }