Skip to content

Commit 1d3ec2a

Browse files
sipavasild
authored andcommitted
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.
1 parent 6af9b31 commit 1d3ec2a

File tree

1 file changed

+17
-5
lines changed

1 file changed

+17
-5
lines changed

src/serialize.h

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@
2424
#include <prevector.h>
2525
#include <span.h>
2626

27-
static const unsigned int MAX_SIZE = 0x02000000;
27+
/**
28+
* The maximum size of a serialized object in bytes or number of elements
29+
* (for eg vectors) when the size is encoded as CompactSize.
30+
*/
31+
static constexpr uint64_t MAX_SIZE = 0x02000000;
2832

2933
/** Maximum amount of memory (in bytes) to allocate at once when deserializing vectors. */
3034
static const unsigned int MAX_VECTOR_ALLOCATE = 5000000;
@@ -304,8 +308,14 @@ void WriteCompactSize(Stream& os, uint64_t nSize)
304308
return;
305309
}
306310

311+
/**
312+
* Decode a CompactSize-encoded variable-length integer.
313+
*
314+
* As these are primarily used to encode the size of vector-like serializations, by default a range
315+
* check is performed. When used as a generic number encoding, range_check should be set to false.
316+
*/
307317
template<typename Stream>
308-
uint64_t ReadCompactSize(Stream& is)
318+
uint64_t ReadCompactSize(Stream& is, bool range_check = true)
309319
{
310320
uint8_t chSize = ser_readdata8(is);
311321
uint64_t nSizeRet = 0;
@@ -331,8 +341,9 @@ uint64_t ReadCompactSize(Stream& is)
331341
if (nSizeRet < 0x100000000ULL)
332342
throw std::ios_base::failure("non-canonical ReadCompactSize()");
333343
}
334-
if (nSizeRet > (uint64_t)MAX_SIZE)
344+
if (range_check && nSizeRet > MAX_SIZE) {
335345
throw std::ios_base::failure("ReadCompactSize(): size too large");
346+
}
336347
return nSizeRet;
337348
}
338349

@@ -466,7 +477,7 @@ static inline Wrapper<Formatter, T&> Using(T&& t) { return Wrapper<Formatter, T&
466477

467478
#define VARINT_MODE(obj, mode) Using<VarIntFormatter<mode>>(obj)
468479
#define VARINT(obj) Using<VarIntFormatter<VarIntMode::DEFAULT>>(obj)
469-
#define COMPACTSIZE(obj) Using<CompactSizeFormatter>(obj)
480+
#define COMPACTSIZE(obj) Using<CompactSizeFormatter<true>>(obj)
470481
#define LIMITED_STRING(obj,n) Using<LimitedStringFormatter<n>>(obj)
471482

472483
/** Serialization wrapper class for integers in VarInt format. */
@@ -529,12 +540,13 @@ struct CustomUintFormatter
529540
template<int Bytes> using BigEndianFormatter = CustomUintFormatter<Bytes, true>;
530541

531542
/** Formatter for integers in CompactSize format. */
543+
template<bool RangeCheck>
532544
struct CompactSizeFormatter
533545
{
534546
template<typename Stream, typename I>
535547
void Unser(Stream& s, I& v)
536548
{
537-
uint64_t n = ReadCompactSize<Stream>(s);
549+
uint64_t n = ReadCompactSize<Stream>(s, RangeCheck);
538550
if (n < std::numeric_limits<I>::min() || n > std::numeric_limits<I>::max()) {
539551
throw std::ios_base::failure("CompactSize exceeds limit of type");
540552
}

0 commit comments

Comments
 (0)