upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/compactblock.cpp
diff options
context:
space:
mode:
authorbitflash-maintainer <noreply@bitflash.invalid>2026-09-05 03:16:12 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-09-05 03:16:12 +0000
commit36201d2795b7b49beb1a837b0252134096beb64d (patch)
tree3283b5ab0b8c41a8707a93dd95d0b5b26afeb9c4 /src/compactblock.cpp
parent60538c5a935787521a5ad8c2a7dce52b5f250f3a (diff)
compactblock: fail closed on RAND_bytes error, bound reconstruction allocation
Two fixes to the file imported in the previous commit. Kept separate so the deviation from upstream's bytes is reviewable on its own. BuildCompactBlock ignored the RAND_bytes return value. On failure nonceBytes is left uninitialised and an indeterminate shortIdNonce is memcpy'd out and sent on the wire -- and that nonce is exactly what the file relies on being unpredictable so short IDs cannot be precomputed before a block exists or ground across blocks. Every other RAND_bytes call site in this tree (bip32.cpp:203, db.cpp:2462, db.cpp:2469) checks for != 1 and fails closed; this one now does too. The caller falls back to full-block relay. TryReconstructBlock sized a vector directly from the wire. nTotal is the sum of two deserialised vector lengths, and serialize.h bounds a vector only by MAX_SIZE (0x02000000), so once the P2P handlers are wired a single small message could ask this node to default-construct ~33M CTransactions. The smallest a transaction can serialise to is 10 bytes, so no block within MAX_BLOCK_SIZE holds more than MAX_BLOCK_SIZE/10 of them; anything above that is not a block this node could accept under any circumstance. Rejecting falls back to full-block relay, which is always correct. Neither is reachable today -- nothing feeds these functions from the network yet -- but both are latent the moment the message handlers land, which is the wrong time to be finding them. test_compactblock (17 checks) and all 14 selftests pass with both in place.
Diffstat (limited to 'src/compactblock.cpp')
-rw-r--r--src/compactblock.cpp22
1 files changed, 21 insertions, 1 deletions
diff --git a/src/compactblock.cpp b/src/compactblock.cpp
index 3999ab7..1ca6118 100644
--- a/src/compactblock.cpp
+++ b/src/compactblock.cpp
@@ -48,8 +48,16 @@ bool BuildCompactBlock(const CBlock& block, CCmpctBlock& out)
48 out.nBits = block.nBits; 48 out.nBits = block.nBits;
49 out.nNonce = block.nNonce; 49 out.nNonce = block.nNonce;
50 50
51 // Upstream ignored the RAND_bytes return value here. On failure nonceBytes
52 // is left uninitialised and an indeterminate shortIdNonce goes out on the
53 // wire -- which is precisely the value this file relies on being
54 // unpredictable to stop short IDs being precomputed or ground against.
55 // Fail closed instead, matching every other RAND_bytes call site in this
56 // tree (bip32.cpp, db.cpp): the caller falls back to full-block relay,
57 // which is always correct.
51 unsigned char nonceBytes[sizeof(uint64)]; 58 unsigned char nonceBytes[sizeof(uint64)];
52 RAND_bytes(nonceBytes, sizeof(nonceBytes)); 59 if (RAND_bytes(nonceBytes, sizeof(nonceBytes)) != 1)
60 return false;
53 memcpy(&out.shortIdNonce, nonceBytes, sizeof(uint64)); 61 memcpy(&out.shortIdNonce, nonceBytes, sizeof(uint64));
54 62
55 out.vPrefilledIndex.clear(); 63 out.vPrefilledIndex.clear();
@@ -75,6 +83,18 @@ bool TryReconstructBlock(const CCmpctBlock& cmpct, CBlock& outBlock, std::vector
75 if (cmpct.vPrefilledIndex.size() != cmpct.vPrefilledTx.size() || nTotal == 0) 83 if (cmpct.vPrefilledIndex.size() != cmpct.vPrefilledTx.size() || nTotal == 0)
76 return false; // malformed 84 return false; // malformed
77 85
86 // nTotal comes straight off the wire once this is wired into the P2P
87 // layer, and serialize.h only bounds a vector length by MAX_SIZE
88 // (0x02000000), so an unbounded assign() below would let one 60-odd-byte
89 // message ask this node for ~33M default-constructed CTransactions. The
90 // smallest a transaction can serialise to is 10 bytes (nVersion, empty
91 // vin, empty vout, nLockTime), so no block within MAX_BLOCK_SIZE can hold
92 // more than MAX_BLOCK_SIZE/10 of them; anything above that is not a block
93 // we could ever accept. Rejecting here just falls back to full-block
94 // relay, which is always correct.
95 if (nTotal > MAX_BLOCK_SIZE / 10)
96 return false; // implausible tx count -- refuse to size a vector from it
97
78 outBlock.SetNull(); 98 outBlock.SetNull();
79 outBlock.nVersion = cmpct.nVersion; 99 outBlock.nVersion = cmpct.nVersion;
80 outBlock.hashPrevBlock = cmpct.hashPrevBlock; 100 outBlock.hashPrevBlock = cmpct.hashPrevBlock;