FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

bitcoin: don't read past a truncated Elements block header · ElementsProject/lightning@8d09ab2 · GitHub

Repository navigation

Commit 8d09ab2

Browse files
committed
bitcoin: don't read past a truncated Elements block header
Changelog-None
1 parent 836b4e8 commit 8d09ab2

2 files changed

Lines changed: 138 additions & 22 deletions

File tree

‎bitcoin/block.c‎

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ static const u8 *pull(const u8 **cursor, size_t *max, void *copy, size_t n)
1111
{
1212
const u8 *p = *cursor;
1313

14-
if (*max < n) {
14+
if (!p || *max < n) {
1515
*cursor = NULL;
1616
*max = 0;
1717
/* Just make sure we don't leak uninitialized mem! */
@@ -60,6 +60,15 @@ static void sha256_varint(struct sha256_ctx *ctx, u64 val)
6060
sha256_update(ctx, vt, vtlen);
6161
}
6262

63+
/* Only hashes the bytes if they were actually there */
64+
static void pull_and_hash(const u8 **cursor, size_t *max,
65+
struct sha256_ctx *shactx, size_t n)
66+
{
67+
const u8 *p = pull(cursor, max, NULL, n);
68+
if (p)
69+
sha256_update(shactx, p, n);
70+
}
71+
6372
static void bitcoin_block_pull_dynafed_params(const u8 **cursor, size_t *len, struct sha256_ctx *shactx)
6473
{
6574
u8 type;
@@ -73,49 +82,41 @@ static void bitcoin_block_pull_dynafed_params(const u8 **cursor, size_t *len, st
7382
/* "scriptPubKey" used for block signing */
7483
l1 = pull_varint(cursor, len);
7584
sha256_varint(shactx, l1);
76-
sha256_update(shactx, *cursor, l1);
77-
pull(cursor, len, NULL, l1);
85+
pull_and_hash(cursor, len, shactx, l1);
7886

7987
/* signblock_witness_limit */
80-
sha256_update(shactx, *cursor, 4);
81-
pull(cursor, len, NULL, 4);
88+
pull_and_hash(cursor, len, shactx, 4);
8289

8390
/* Skip elided_root */
84-
sha256_update(shactx, *cursor, 32);
85-
pull(cursor, len, NULL, 32);
91+
pull_and_hash(cursor, len, shactx, 32);
8692
break;
8793

8894
case DYNAFED_PARAMS_FULL:
8995
/* "scriptPubKey" used for block signing */
9096
l1 = pull_varint(cursor, len);
9197
sha256_varint(shactx, l1);
92-
sha256_update(shactx, *cursor, l1);
93-
pull(cursor, len, NULL, l1);
98+
pull_and_hash(cursor, len, shactx, l1);
9499

95100
/* signblock_witness_limit */
96-
sha256_update(shactx, *cursor, 4);
97-
pull(cursor, len, NULL, 4);
101+
pull_and_hash(cursor, len, shactx, 4);
98102

99103
/* fedpeg_program */
100104
l1 = pull_varint(cursor, len);
101105
sha256_varint(shactx, l1);
102-
sha256_update(shactx, *cursor, l1);
103-
pull(cursor, len, NULL, l1);
106+
pull_and_hash(cursor, len, shactx, l1);
104107

105108
/* fedpegscript */
106109
l1 = pull_varint(cursor, len);
107110
sha256_varint(shactx, l1);
108-
sha256_update(shactx, *cursor, l1);
109-
pull(cursor, len, NULL, l1);
111+
pull_and_hash(cursor, len, shactx, l1);
110112

111-
/* extension space */
113+
/* extension space: stop once we've run out of data. */
112114
l2 = pull_varint(cursor, len);
113115
sha256_varint(shactx, l2);
114-
for (size_t i = 0; i < l2; i++) {
116+
for (size_t i = 0; i < l2 && *cursor; i++) {
115117
l1 = pull_varint(cursor, len);
116118
sha256_varint(shactx, l1);
117-
sha256_update(shactx, *cursor, l1);
118-
pull(cursor, len, NULL, l1);
119+
pull_and_hash(cursor, len, shactx, l1);
119120
}
120121
break;
121122
}
@@ -128,7 +129,7 @@ static void bitcoin_block_pull_dynafed_details(const u8 **cursor, size_t *len, s
128129

129130
/* Consume the signblock_witness */
130131
u64 numwitnesses = pull_varint(cursor, len);
131-
for (size_t i=0; i<numwitnesses; i++) {
132+
for (size_t i=0; i<numwitnesses && *cursor; i++) {
132133
u64 witsize = pull_varint(cursor, len);
133134
pull(cursor, len, NULL, witsize);
134135
}
@@ -187,8 +188,7 @@ bitcoin_block_from_hex(const tal_t *ctx, const struct chainparams *chainparams,
187188
/* elemens_header.challenge */
188189
templen = pull_varint(&p, &len);
189190
sha256_varint(&shactx, templen);
190-
sha256_update(&shactx, p, templen);
191-
pull(&p, &len, NULL, templen);
191+
pull_and_hash(&p, &len, &shactx, templen);
192192

193193
/* elements_header.solution. Not hashed since it'd be
194194
* a circular dependency. */
@@ -206,6 +206,9 @@ bitcoin_block_from_hex(const tal_t *ctx, const struct chainparams *chainparams,
206206
sha256_double_done(&shactx, &b->hdr.hash.shad);
207207

208208
num = pull_varint(&p, &len);
209+
/* Every tx takes at least one byte - dont let a bogus count make us allocate a huge array */
210+
if (num > len)
211+
return tal_free(b);
209212
b->tx = tal_arr(b, struct bitcoin_tx *, num);
210213
b->txids = tal_arr(b, struct bitcoin_txid, num);
211214
for (i = 0; i < num; i++) {

‎bitcoin/test/run-bitcoin_block_from_hex.c‎

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,117 @@ static const char block[] =
6262

6363
STRUCTEQ_DEF(sha256_double, 0, sha);
6464

65+
static void add_bytes(u8 **buf, u8 byte, size_t n)
66+
{
67+
size_t off = tal_count(*buf);
68+
tal_resize(buf, off + n);
69+
memset(*buf + off, byte, n);
70+
}
71+
72+
/* Elements header up to (and including) the version, hashes, timestamp
73+
* and height. */
74+
static u8 *elements_header_start(bool dynafed)
75+
{
76+
u8 *buf = tal_arr(tmpctx, u8, 0);
77+
78+
/* version: the MSB signals dynafed */
79+
add_bytes(&buf, 0, 3);
80+
add_bytes(&buf, dynafed ? 0x80 : 0, 1);
81+
/* prev_hash, merkle_hash, timestamp, height */
82+
add_bytes(&buf, 0, 32 + 32 + 4 + 4);
83+
return buf;
84+
}
85+
86+
/* Dynafed header up to (and including) the current params' fedpegscript. */
87+
static u8 *dynafed_header_to_fedpegscript(void)
88+
{
89+
u8 *buf = elements_header_start(true);
90+
91+
add_bytes(&buf, DYNAFED_PARAMS_FULL, 1);
92+
/* signblockscript */
93+
add_varint(&buf, 2);
94+
add_bytes(&buf, 0x51, 2);
95+
/* signblock_witness_limit */
96+
add_bytes(&buf, 0, 4);
97+
/* fedpeg_program */
98+
add_varint(&buf, 1);
99+
add_bytes(&buf, 0x51, 1);
100+
/* fedpegscript */
101+
add_varint(&buf, 1);
102+
add_bytes(&buf, 0x51, 1);
103+
return buf;
104+
}
105+
106+
static struct bitcoin_block *parse(const struct chainparams *cp,
107+
const u8 *buf, size_t len)
108+
{
109+
const char *hex = tal_hexstr(tmpctx, buf, len);
110+
return bitcoin_block_from_hex(tmpctx, cp, hex, strlen(hex));
111+
}
112+
113+
/* A valid empty block must parse; every truncation of it must be refused. */
114+
static void check_truncations(const struct chainparams *cp, const u8 *buf)
115+
{
116+
struct bitcoin_block *b = parse(cp, buf, tal_bytelen(buf));
117+
assert(b);
118+
assert(tal_count(b->tx) == 0);
119+
120+
for (size_t i = 0; i < tal_bytelen(buf); i++)
121+
assert(!parse(cp, buf, i));
122+
}
123+
124+
static void test_elements(void)
125+
{
126+
const struct chainparams *cp = chainparams_for_network("liquid-regtest");
127+
u8 *buf;
128+
129+
/* Non-dynafed: challenge, solution, then no txs. */
130+
buf = elements_header_start(false);
131+
add_varint(&buf, 1);
132+
add_bytes(&buf, 0x51, 1);
133+
add_varint(&buf, 1);
134+
add_bytes(&buf, 0, 1);
135+
add_varint(&buf, 0);
136+
check_truncations(cp, buf);
137+
138+
/* Dynafed: full current params, compact proposed params,
139+
* one signblock witness, then no txs. */
140+
buf = dynafed_header_to_fedpegscript();
141+
/* extension space */
142+
add_varint(&buf, 1);
143+
add_varint(&buf, 1);
144+
add_bytes(&buf, 0x51, 1);
145+
/* proposed params: signblockscript, witness limit, elided_root */
146+
add_bytes(&buf, DYNAFED_PARAMS_COMPACT, 1);
147+
add_varint(&buf, 1);
148+
add_bytes(&buf, 0x51, 1);
149+
add_bytes(&buf, 0, 4 + 32);
150+
/* signblock_witness */
151+
add_varint(&buf, 1);
152+
add_varint(&buf, 1);
153+
add_bytes(&buf, 0, 1);
154+
add_varint(&buf, 0);
155+
check_truncations(cp, buf);
156+
157+
/* A huge extension space count with no data must not spin. */
158+
buf = dynafed_header_to_fedpegscript();
159+
add_varint(&buf, SIZE_MAX);
160+
assert(!parse(cp, buf, tal_bytelen(buf)));
161+
162+
/* Same for a huge signblock_witness count. */
163+
buf = elements_header_start(true);
164+
add_bytes(&buf, DYNAFED_PARAMS_NULL, 2);
165+
add_varint(&buf, SIZE_MAX);
166+
assert(!parse(cp, buf, tal_bytelen(buf)));
167+
}
168+
65169
int main(int argc, const char *argv[])
66170
{
67171
struct bitcoin_blkid prev;
68172
struct sha256_double merkle;
69173
struct bitcoin_txid txid, expected_txid;
70174
struct bitcoin_block *b;
175+
const char *hex;
71176

72177
common_setup(argv[0]);
73178
chainparams = chainparams_for_network("bitcoin");
@@ -103,6 +208,14 @@ int main(int argc, const char *argv[])
103208
assert(bitcoin_txid_eq(&txid, &expected_txid));
104209

105210
tal_free(b);
211+
212+
/* The 80-byte header followed by a huge tx count must be refused,
213+
* not abort trying to allocate the tx array. */
214+
hex = tal_fmt(tmpctx, "%.160sffffffffffffffffff", block);
215+
assert(!bitcoin_block_from_hex(tmpctx, chainparams, hex, strlen(hex)));
216+
217+
test_elements();
218+
106219
common_shutdown();
107220
return 0;
108221
}

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL