Skip to content

Commit cfc10b1

Browse files
committed
fix: harden PSBT signing against integer UB on malformed input
1 parent e9ccf96 commit cfc10b1

5 files changed

Lines changed: 26 additions & 17 deletions

File tree

src/handler/lib/check_merkle_tree_sorted.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ int call_check_merkle_tree_sorted_with_callback(dispatcher_context_t *dispatcher
2424

2525
for (size_t cur_el_idx = 0; cur_el_idx < size; cur_el_idx++) {
2626
uint8_t cur_el[MAX_CHECK_MERKLE_TREE_SORTED_PREIMAGE_SIZE];
27+
// tree size and leaf index are bounded well below 2^32 in practice;
28+
// cast to the callee's uint32_t parameters explicitly.
2729
int cur_el_len = call_get_merkle_leaf_element(dispatcher_context,
2830
root,
29-
size,
30-
cur_el_idx,
31+
(uint32_t) size,
32+
(uint32_t) cur_el_idx,
3133
cur_el,
3234
sizeof(cur_el));
3335

src/handler/lib/policy.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1956,7 +1956,7 @@ static int check_older_node_cb(const policy_node_t *node, void *callback_state)
19561956
(void) callback_state;
19571957
if (node->type == TOKEN_OLDER) {
19581958
const policy_node_with_uint32_t *older = (const policy_node_with_uint32_t *) node;
1959-
uint32_t n = older->n & ~SEQUENCE_LOCKTIME_TYPE_FLAG;
1959+
uint32_t n = older->n & ~(uint32_t) SEQUENCE_LOCKTIME_TYPE_FLAG;
19601960
if (n < 1 || n > 65535) {
19611961
return -1;
19621962
}

src/handler/sign_psbt.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ typedef struct {
3434
// matched with the current key expression in the signing flow
3535

3636
bool is_change;
37-
int address_index;
37+
uint32_t address_index;
3838

3939
// For an output, its scriptPubKey
4040
// for an input, the prevout's scriptPubKey (either from the non-witness-utxo, or from the

src/handler/sign_psbt/musig_signing.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -368,12 +368,14 @@ bool __attribute__((noinline)) sign_sighash_musig_and_yield(dispatcher_context_t
368368
memcpy(musig_my_psbt_id + 33 + 33, keyexpr_info->tapleaf_hash, 32);
369369
}
370370
musig_pubnonce_t my_pubnonce;
371-
if (sizeof(musig_pubnonce_t) != call_get_merkleized_map_value(dc,
372-
&input->in_out.map,
373-
musig_my_psbt_id_key,
374-
1 + psbt_id_len,
375-
my_pubnonce.raw,
376-
sizeof(musig_pubnonce_t))) {
371+
// call_get_merkleized_map_value returns int (negative on error); cast the
372+
// unsigned sizeof so the comparison doesn't trip UBSan's sign-change check.
373+
if ((int) sizeof(musig_pubnonce_t) != call_get_merkleized_map_value(dc,
374+
&input->in_out.map,
375+
musig_my_psbt_id_key,
376+
1 + psbt_id_len,
377+
my_pubnonce.raw,
378+
sizeof(musig_pubnonce_t))) {
377379
PRINTF("Missing or erroneous pubnonce in PSBT\n");
378380
SEND_SW(dc, SW_INCORRECT_DATA);
379381
return false;

src/handler/sign_psbt/preprocess_inputs.c

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,12 @@ bool __attribute__((noinline)) preprocess_inputs(
175175
return false;
176176
}
177177

178+
// sanity check before accumulating, to avoid overflowing the total
179+
if (input.prevout_amount > BITCOIN_TOTAL_SUPPLY) {
180+
PRINTF("Input amount exceeds Bitcoin total supply!\n");
181+
SEND_SW(dc, SW_INCORRECT_DATA);
182+
return false;
183+
}
178184
st->inputs_total_amount += input.prevout_amount;
179185
}
180186

@@ -209,6 +215,12 @@ bool __attribute__((noinline)) preprocess_inputs(
209215
}
210216
} else {
211217
// we extract the scriptPubKey and prevout amount from the witness utxo
218+
// sanity check before accumulating, to avoid overflowing the total
219+
if (wit_utxo_prevout_amount > BITCOIN_TOTAL_SUPPLY) {
220+
PRINTF("Input amount exceeds Bitcoin total supply!\n");
221+
SEND_SW(dc, SW_INCORRECT_DATA);
222+
return false;
223+
}
212224
st->inputs_total_amount += wit_utxo_prevout_amount;
213225

214226
input.prevout_amount = wit_utxo_prevout_amount;
@@ -217,13 +229,6 @@ bool __attribute__((noinline)) preprocess_inputs(
217229
}
218230
}
219231

220-
if (input.prevout_amount > BITCOIN_TOTAL_SUPPLY) {
221-
// sanity check to avoid overflows in amounts
222-
PRINTF("Input amount exceed Bitcoin total supply!\n");
223-
SEND_SW(dc, SW_INCORRECT_DATA);
224-
return false;
225-
}
226-
227232
// check if the input is internal; if not, continue
228233

229234
int is_internal = is_in_out_internal(dc, st, sign_psbt_cache, &input.in_out, true);

0 commit comments

Comments
 (0)