diff --git a/bitcoin/block.c b/bitcoin/block.c index 6838f2c39344..9afee45df726 100644 --- a/bitcoin/block.c +++ b/bitcoin/block.c @@ -1,5 +1,4 @@ #include "config.h" -#include #include #include #include @@ -11,7 +10,9 @@ static const u8 *pull(const u8 **cursor, size_t *max, void *copy, size_t n) { const u8 *p = *cursor; - if (*max < n) { + /* Once a pull has overrun, the cursor stays NULL: a later zero-length + * pull must not resume from it. */ + if (!p || *max < n) { *cursor = NULL; *max = 0; /* Just make sure we don't leak uninitialized mem! */ @@ -21,7 +22,6 @@ static const u8 *pull(const u8 **cursor, size_t *max, void *copy, size_t n) } *cursor += n; *max -= n; - assert(p); if (copy) memcpy(copy, p, n); return memcheck(p, n); @@ -60,6 +60,16 @@ static void sha256_varint(struct sha256_ctx *ctx, u64 val) sha256_update(ctx, vt, vtlen); } +/* Hashing before the pull reads past the end of a truncated block, so let + * pull() say whether the bytes are there. */ +static void pull_and_hash(const u8 **cursor, size_t *len, + struct sha256_ctx *shactx, size_t n) +{ + const u8 *p = pull(cursor, len, NULL, n); + if (p) + sha256_update(shactx, p, n); +} + static void bitcoin_block_pull_dynafed_params(const u8 **cursor, size_t *len, struct sha256_ctx *shactx) { u8 type; @@ -73,49 +83,41 @@ static void bitcoin_block_pull_dynafed_params(const u8 **cursor, size_t *len, st /* "scriptPubKey" used for block signing */ l1 = pull_varint(cursor, len); sha256_varint(shactx, l1); - sha256_update(shactx, *cursor, l1); - pull(cursor, len, NULL, l1); + pull_and_hash(cursor, len, shactx, l1); /* signblock_witness_limit */ - sha256_update(shactx, *cursor, 4); - pull(cursor, len, NULL, 4); + pull_and_hash(cursor, len, shactx, 4); /* Skip elided_root */ - sha256_update(shactx, *cursor, 32); - pull(cursor, len, NULL, 32); + pull_and_hash(cursor, len, shactx, 32); break; case DYNAFED_PARAMS_FULL: /* "scriptPubKey" used for block signing */ l1 = pull_varint(cursor, len); sha256_varint(shactx, l1); - sha256_update(shactx, *cursor, l1); - pull(cursor, len, NULL, l1); + pull_and_hash(cursor, len, shactx, l1); /* signblock_witness_limit */ - sha256_update(shactx, *cursor, 4); - pull(cursor, len, NULL, 4); + pull_and_hash(cursor, len, shactx, 4); /* fedpeg_program */ l1 = pull_varint(cursor, len); sha256_varint(shactx, l1); - sha256_update(shactx, *cursor, l1); - pull(cursor, len, NULL, l1); + pull_and_hash(cursor, len, shactx, l1); /* fedpegscript */ l1 = pull_varint(cursor, len); sha256_varint(shactx, l1); - sha256_update(shactx, *cursor, l1); - pull(cursor, len, NULL, l1); + pull_and_hash(cursor, len, shactx, l1); /* extension space */ l2 = pull_varint(cursor, len); sha256_varint(shactx, l2); - for (size_t i = 0; i < l2; i++) { + for (size_t i = 0; i < l2 && *cursor; i++) { l1 = pull_varint(cursor, len); sha256_varint(shactx, l1); - sha256_update(shactx, *cursor, l1); - pull(cursor, len, NULL, l1); + pull_and_hash(cursor, len, shactx, l1); } break; } @@ -128,7 +130,7 @@ static void bitcoin_block_pull_dynafed_details(const u8 **cursor, size_t *len, s /* Consume the signblock_witness */ u64 numwitnesses = pull_varint(cursor, len); - for (size_t i=0; ihdr.hash.shad); num = pull_varint(&p, &len); + /* Every transaction takes at least one byte */ + if (num > len) + return tal_free(b); b->tx = tal_arr(b, struct bitcoin_tx *, num); b->txids = tal_arr(b, struct bitcoin_txid, num); for (i = 0; i < num; i++) { b->tx[i] = pull_bitcoin_tx_only(b->tx, &p, &len); + if (!b->tx[i]) + return tal_free(b); b->tx[i]->chainparams = chainparams; bitcoin_txid(b->tx[i], &b->txids[i]); } diff --git a/bitcoin/test/run-bitcoin_block_from_hex.c b/bitcoin/test/run-bitcoin_block_from_hex.c index 94b26ad5d05f..3e2a6228912a 100644 --- a/bitcoin/test/run-bitcoin_block_from_hex.c +++ b/bitcoin/test/run-bitcoin_block_from_hex.c @@ -60,6 +60,29 @@ static const char block[] = "ac0eb82500000000001976a914e05655a7d90b01ba874d81beff57ee09610ca" "3ce88ac00000000"; +/* Elements header whose challenge varint promises 80 bytes with none + * left in the block. */ +static const char elements_short_challenge[] = + "0000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000afdc6a5c6400000050"; + +/* Dynafed header whose scriptPubKey varint promises 80 bytes with none + * left in the block. */ +static const char elements_short_dynafed[] = + "000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000afdc6a5c640000000150"; + +/* Block whose only transaction does not parse. */ +static const char bad_tx[] = + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ff"; + +/* Block claiming far more transactions than it has bytes. */ +static const char huge_tx_count[] = + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff7f"; + +/* Dynafed header whose extension space claims far more entries than it + * has bytes. */ +static const char elements_huge_extension[] = + "000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000ffffffffffffffff7f"; + STRUCTEQ_DEF(sha256_double, 0, sha); int main(int argc, const char *argv[]) @@ -70,7 +93,23 @@ int main(int argc, const char *argv[]) struct bitcoin_block *b; common_setup(argv[0]); + chainparams = chainparams_for_network("liquid-regtest"); + assert(!bitcoin_block_from_hex(NULL, chainparams, + elements_short_challenge, + strlen(elements_short_challenge))); + assert(!bitcoin_block_from_hex(NULL, chainparams, + elements_short_dynafed, + strlen(elements_short_dynafed))); + assert(!bitcoin_block_from_hex(NULL, chainparams, + elements_huge_extension, + strlen(elements_huge_extension))); + chainparams = chainparams_for_network("bitcoin"); + assert(!bitcoin_block_from_hex(NULL, chainparams, + bad_tx, strlen(bad_tx))); + assert(!bitcoin_block_from_hex(NULL, chainparams, + huge_tx_count, strlen(huge_tx_count))); + b = bitcoin_block_from_hex(NULL, chainparams, block, strlen(block));