diff options
author | Pieter Wuille <pieter@wuille.net> | 2020-10-08 20:27:27 -0700 |
---|---|---|
committer | Vasil Dimov <vd@FreeBSD.org> | 2020-10-09 10:32:19 +0200 |
commit | 1d3ec2a1fda7446323786a52da1fd109c01aa6fb (patch) | |
tree | 7d4dd26269bf40b771e360c64ca3a12388ec5880 | |
parent | 6af9b31bfc6dd6086d245c00eb7cff762fc34a1e (diff) |
Support bypassing range check in ReadCompactSize
This is needed when we want to encode an arbitrary number as CompactSize
like node service flags, which is a bitmask and could be bigger than the
usual size of an object.
-rw-r--r-- | src/serialize.h | 22 |
1 files changed, 17 insertions, 5 deletions
diff --git a/src/serialize.h b/src/serialize.h index 7a94e704b2..d9ca984f9c 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -24,7 +24,11 @@ #include <prevector.h> #include <span.h> -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; @@ -304,8 +308,14 @@ void WriteCompactSize(Stream& os, uint64_t nSize) return; } +/** + * 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<typename Stream> -uint64_t ReadCompactSize(Stream& is) +uint64_t ReadCompactSize(Stream& is, bool range_check = true) { uint8_t chSize = ser_readdata8(is); uint64_t nSizeRet = 0; @@ -331,8 +341,9 @@ uint64_t ReadCompactSize(Stream& is) if (nSizeRet < 0x100000000ULL) throw std::ios_base::failure("non-canonical ReadCompactSize()"); } - if (nSizeRet > (uint64_t)MAX_SIZE) + if (range_check && nSizeRet > MAX_SIZE) { throw std::ios_base::failure("ReadCompactSize(): size too large"); + } return nSizeRet; } @@ -466,7 +477,7 @@ static inline Wrapper<Formatter, T&> Using(T&& t) { return Wrapper<Formatter, T& #define VARINT_MODE(obj, mode) Using<VarIntFormatter<mode>>(obj) #define VARINT(obj) Using<VarIntFormatter<VarIntMode::DEFAULT>>(obj) -#define COMPACTSIZE(obj) Using<CompactSizeFormatter>(obj) +#define COMPACTSIZE(obj) Using<CompactSizeFormatter<true>>(obj) #define LIMITED_STRING(obj,n) Using<LimitedStringFormatter<n>>(obj) /** Serialization wrapper class for integers in VarInt format. */ @@ -529,12 +540,13 @@ struct CustomUintFormatter template<int Bytes> using BigEndianFormatter = CustomUintFormatter<Bytes, true>; /** Formatter for integers in CompactSize format. */ +template<bool RangeCheck> struct CompactSizeFormatter { template<typename Stream, typename I> void Unser(Stream& s, I& v) { - uint64_t n = ReadCompactSize<Stream>(s); + uint64_t n = ReadCompactSize<Stream>(s, RangeCheck); if (n < std::numeric_limits<I>::min() || n > std::numeric_limits<I>::max()) { throw std::ios_base::failure("CompactSize exceeds limit of type"); } |