diff options
author | Gavin Andresen <gavinandresen@gmail.com> | 2013-08-07 12:21:34 +1000 |
---|---|---|
committer | Gavin Andresen <gavinandresen@gmail.com> | 2013-08-09 10:01:35 +1000 |
commit | 8dc206a1e2715be83912e039465a049b708b94c1 (patch) | |
tree | c5f8682aeca86134da42cea9dae729faf03ef73a /src/serialize.h | |
parent | ddd0e2f616be82fb57d68298be796c34268c6ae9 (diff) |
Reject non-canonically-encoded sizes
The length of vectors, maps, sets, etc are serialized using
Write/ReadCompactSize -- which, unfortunately, do not use a
unique encoding.
So deserializing and then re-serializing a transaction (for example)
can give you different bits than you started with. That doesn't
cause any problems that we are aware of, but it is exactly the type
of subtle mismatch that can lead to exploits.
With this pull, reading a non-canonical CompactSize throws an
exception, which means nodes will ignore 'tx' or 'block' or
other messages that are not properly encoded.
Please check my logic... but this change is safe with respect to
causing a network split. Old clients that receive
non-canonically-encoded transactions or blocks deserialize
them into CTransaction/CBlock structures in memory, and then
re-serialize them before relaying them to peers.
And please check my logic with respect to causing a blockchain
split: there are no CompactSize fields in the block header, so
the block hash is always canonical. The merkle root in the block
header is computed on a vector<CTransaction>, so
any non-canonical encoding of the transactions in 'tx' or 'block'
messages is erased as they are read into memory by old clients,
and does not affect the block hash. And, as noted above, old
clients re-serialize (with canonical encoding) 'tx' and 'block'
messages before relaying to peers.
Diffstat (limited to 'src/serialize.h')
-rw-r--r-- | src/serialize.h | 6 |
1 files changed, 6 insertions, 0 deletions
diff --git a/src/serialize.h b/src/serialize.h index e3d9939bcc..ca3c962240 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -216,18 +216,24 @@ uint64 ReadCompactSize(Stream& is) unsigned short xSize; READDATA(is, xSize); nSizeRet = xSize; + if (nSizeRet < 253) + throw std::ios_base::failure("non-canonical ReadCompactSize()"); } else if (chSize == 254) { unsigned int xSize; READDATA(is, xSize); nSizeRet = xSize; + if (nSizeRet < 0x10000u) + throw std::ios_base::failure("non-canonical ReadCompactSize()"); } else { uint64 xSize; READDATA(is, xSize); nSizeRet = xSize; + if (nSizeRet < 0x100000000LLu) + throw std::ios_base::failure("non-canonical ReadCompactSize()"); } if (nSizeRet > (uint64)MAX_SIZE) throw std::ios_base::failure("ReadCompactSize() : size too large"); |