Skip to content

Commit

Permalink
Support bypassing range check in ReadCompactSize
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sipa authored and furszy committed Aug 10, 2021
1 parent a237ba4 commit 3eaa273
Showing 1 changed file with 19 additions and 7 deletions.
26 changes: 19 additions & 7 deletions src/serialize.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -334,8 +338,14 @@ void WriteCompactSize(Stream& os, uint64_t nSize)
return;
}

template <typename Stream>
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<typename Stream>
uint64_t ReadCompactSize(Stream& is, bool range_check = true)
{
uint8_t chSize = ser_readdata8(is);
uint64_t nSizeRet = 0;
Expand All @@ -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;
}

Expand Down Expand Up @@ -489,7 +500,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. */
Expand Down Expand Up @@ -552,12 +563,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");
}
Expand Down

0 comments on commit 3eaa273

Please sign in to comment.