Files
palladum-lightning/common/htlc_wire.c
Lagrang3 7e5cf41b4e htlc_wire: fix crash when adding an HTLC
In line channeld/channeld_wiregen.c:832 `*added+i` is not a tal object hence
the instruction in common/htlc_wire.c:200 `tal_arr(ctx, struct tlv_field, 0);` crashes CLN.
This is fixed by stating that added_htlc is a a varsize_type.

Logs:

2025-08-16T02:25:28.640Z **BROKEN** lightningd: FATAL SIGNAL 6 (version v25.05-200-g79b959b)V
...
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: ccan/ccan/tal/tal.c:95 (call_error) 0x54f6bc
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: ccan/ccan/tal/tal.c:169 (check_bounds) 0x54f75a
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: ccan/ccan/tal/tal.c:178 (to_tal_hdr) 0x54f782
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: ccan/ccan/tal/tal.c:193 (to_tal_hdr_or_null) 0x54f7c7
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: ccan/ccan/tal/tal.c:471 (tal_alloc_) 0x54ffe4
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: ccan/ccan/tal/tal.c:517 (tal_alloc_arr_) 0x5500c4
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: common/htlc_wire.c:200 (fromwire_len_and_tlvstream) 0x48d63d
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: common/htlc_wire.c:234 (fromwire_added_htlc) 0x48dd23
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: channeld/channeld_wiregen.c:832 (fromwire_channeld_got_commitsig) 0x4c61fa
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: lightningd/peer_htlcs.c:2377 (peer_got_commitsig) 0x4549cb
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: lightningd/channel_control.c:1552 (channel_msg) 0x4140fe
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: lightningd/subd.c:560 (sd_msg_read) 0x461513
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: ccan/ccan/io/io.c:60 (next_plan) 0x544885
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: ccan/ccan/io/io.c:422 (do_plan) 0x544cea
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: ccan/ccan/io/io.c:439 (io_ready) 0x544d9d
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: ccan/ccan/io/poll.c:455 (io_loop) 0x54665d
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: lightningd/io_loop_with_timers.c:22 (io_loop_with_timers) 0x42d220
2025-08-16T02:25:28.640Z **BROKEN** lightningd: backtrace: lightningd/lightningd.c:1487 (main) 0x43280f

gdb inspection:
830             *added = num_added ? tal_arr(ctx, struct added_htlc, num_added) : NULL;
831             for (size_t i = 0; i < num_added; i++)
832                     fromwire_added_htlc(&cursor, &plen, *added + i);
(gdb) p i
$3 = 1

Changelog-None: crash introduced this release.
Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
[ Added test, removed Changelog --RR ]
2025-08-27 14:14:31 +09:30

347 lines
10 KiB
C

#include "config.h"
#include <ccan/array_size/array_size.h>
#include <ccan/cast/cast.h>
#include <ccan/crypto/shachain/shachain.h>
#include <common/htlc_wire.h>
#include <common/onionreply.h>
#include <wire/tlvstream.h>
static struct failed_htlc *failed_htlc_dup(const tal_t *ctx,
const struct failed_htlc *f TAKES)
{
struct failed_htlc *newf;
if (taken(f))
return cast_const(struct failed_htlc *, tal_steal(ctx, f));
newf = tal(ctx, struct failed_htlc);
newf->id = f->id;
newf->sha256_of_onion = tal_dup_or_null(newf, struct sha256,
f->sha256_of_onion);
newf->badonion = f->badonion;
if (f->onion)
newf->onion = dup_onionreply(newf, f->onion);
else
newf->onion = NULL;
return newf;
}
/* Helper to duplicate an array of tlv_field (vs an array of tlv_field *) */
struct tlv_field *tlv_field_arr_dup(const tal_t *ctx,
const struct tlv_field *arr TAKES)
{
struct tlv_field *ret;
bool needs_copy = !is_taken(arr);
ret = tal_dup_talarr(ctx, struct tlv_field, arr);
if (needs_copy) {
for (size_t i = 0; i < tal_count(ret); i++) {
/* We need to attach the value to the correct parent */
ret[i].value = tal_dup_talarr(ret, u8, ret[i].value);
}
}
return ret;
}
struct existing_htlc *new_existing_htlc(const tal_t *ctx,
u64 id,
enum htlc_state state,
struct amount_msat amount,
const struct sha256 *payment_hash,
u32 cltv_expiry,
const u8 onion_routing_packet[TOTAL_PACKET_SIZE(ROUTING_INFO_SIZE)],
const struct pubkey *path_key TAKES,
const struct preimage *preimage TAKES,
const struct failed_htlc *failed TAKES,
const struct tlv_field *extra_tlvs TAKES)
{
struct existing_htlc *existing = tal(ctx, struct existing_htlc);
existing->id = id;
existing->state = state;
existing->amount = amount;
existing->cltv_expiry = cltv_expiry;
existing->payment_hash = *payment_hash;
memcpy(existing->onion_routing_packet, onion_routing_packet,
sizeof(existing->onion_routing_packet));
existing->path_key = tal_dup_or_null(existing, struct pubkey, path_key);
existing->payment_preimage
= tal_dup_or_null(existing, struct preimage, preimage);
if (failed)
existing->failed = failed_htlc_dup(existing, failed);
else
existing->failed = NULL;
if (extra_tlvs)
existing->extra_tlvs = tlv_field_arr_dup(existing, extra_tlvs);
else
existing->extra_tlvs = NULL;
return existing;
}
static void towire_len_and_tlvstream(u8 **pptr, struct tlv_field *extra_tlvs)
{
/* Making a copy is a bit awful, but it's the easiest way to
* get the length */
u8 *tmp_pptr = tal_arr(tmpctx, u8, 0);
towire_tlvstream_raw(&tmp_pptr, extra_tlvs);
assert(tal_bytelen(tmp_pptr) == (u16)tal_bytelen(tmp_pptr));
towire_u16(pptr, tal_bytelen(tmp_pptr));
towire_u8_array(pptr, tmp_pptr, tal_bytelen(tmp_pptr));
}
/* FIXME: We could adapt tools/generate-wire.py to generate structures
* and code like this. */
void towire_added_htlc(u8 **pptr, const struct added_htlc *added)
{
towire_u64(pptr, added->id);
towire_amount_msat(pptr, added->amount);
towire_sha256(pptr, &added->payment_hash);
towire_u32(pptr, added->cltv_expiry);
towire(pptr, added->onion_routing_packet,
sizeof(added->onion_routing_packet));
if (added->path_key) {
towire_bool(pptr, true);
towire_pubkey(pptr, added->path_key);
} else
towire_bool(pptr, false);
if (added->extra_tlvs) {
towire_bool(pptr, true);
towire_len_and_tlvstream(pptr, added->extra_tlvs);
} else
towire_bool(pptr, false);
towire_bool(pptr, added->fail_immediate);
}
void towire_existing_htlc(u8 **pptr, const struct existing_htlc *existing)
{
towire_u8(pptr, existing->state);
towire_u64(pptr, existing->id);
towire_amount_msat(pptr, existing->amount);
towire_sha256(pptr, &existing->payment_hash);
towire_u32(pptr, existing->cltv_expiry);
towire(pptr, existing->onion_routing_packet,
sizeof(existing->onion_routing_packet));
if (existing->payment_preimage) {
towire_bool(pptr, true);
towire_preimage(pptr, existing->payment_preimage);
} else
towire_bool(pptr, false);
if (existing->failed) {
towire_bool(pptr, true);
towire_failed_htlc(pptr, existing->failed);
} else
towire_bool(pptr, false);
if (existing->path_key) {
towire_bool(pptr, true);
towire_pubkey(pptr, existing->path_key);
} else
towire_bool(pptr, false);
if (existing->extra_tlvs) {
towire_bool(pptr, true);
towire_len_and_tlvstream(pptr, existing->extra_tlvs);
} else
towire_bool(pptr, false);
}
void towire_fulfilled_htlc(u8 **pptr, const struct fulfilled_htlc *fulfilled)
{
towire_u64(pptr, fulfilled->id);
towire_preimage(pptr, &fulfilled->payment_preimage);
}
void towire_failed_htlc(u8 **pptr, const struct failed_htlc *failed)
{
towire_u64(pptr, failed->id);
/* Only one can be set. */
if (failed->sha256_of_onion) {
assert(!failed->onion);
assert(failed->badonion & BADONION);
towire_u16(pptr, failed->badonion);
towire_sha256(pptr, failed->sha256_of_onion);
} else {
towire_u16(pptr, 0);
towire_onionreply(pptr, failed->onion);
}
}
static void towire_htlc_state(u8 **pptr, const enum htlc_state hstate)
{
towire_u8(pptr, hstate);
}
void towire_changed_htlc(u8 **pptr, const struct changed_htlc *changed)
{
towire_htlc_state(pptr, changed->newstate);
towire_u64(pptr, changed->id);
}
void towire_side(u8 **pptr, const enum side side)
{
towire_u8(pptr, side);
}
void towire_shachain(u8 **pptr, const struct shachain *shachain)
{
size_t i;
towire_u64(pptr, shachain->min_index);
towire_u32(pptr, shachain->num_valid);
for (i = 0; i < shachain->num_valid; i++) {
towire_u64(pptr, shachain->known[i].index);
towire_sha256(pptr, &shachain->known[i].hash);
}
}
static struct tlv_field *fromwire_len_and_tlvstream(const tal_t *ctx,
const u8 **cursor, size_t *max)
{
struct tlv_field *tlvs = tal_arr(ctx, struct tlv_field, 0);
size_t len = fromwire_u16(cursor, max);
/* Subtle: we are not using fromwire_tal_arrn here, which
* would do this. */
if (len > *max) {
fromwire_fail(cursor, max);
return NULL;
}
/* NOTE: We might consider to be more strict and only allow for
* known tlv types from the tlvs_tlv_update_add_htlc_tlvs
* record. */
if (!fromwire_tlv(cursor, &len, NULL, 0, cast_const(void *, ctx),
&tlvs, FROMWIRE_TLV_ANY_TYPE, NULL, NULL))
return tal_free(tlvs);
return tlvs;
}
struct added_htlc *fromwire_added_htlc(const tal_t *ctx, const u8 **cursor,
size_t *max)
{
struct added_htlc *added = tal(ctx, struct added_htlc);
added->id = fromwire_u64(cursor, max);
added->amount = fromwire_amount_msat(cursor, max);
fromwire_sha256(cursor, max, &added->payment_hash);
added->cltv_expiry = fromwire_u32(cursor, max);
fromwire(cursor, max, added->onion_routing_packet,
sizeof(added->onion_routing_packet));
if (fromwire_bool(cursor, max)) {
added->path_key = tal(added, struct pubkey);
fromwire_pubkey(cursor, max, added->path_key);
} else
added->path_key = NULL;
if (fromwire_bool(cursor, max)) {
added->extra_tlvs = fromwire_len_and_tlvstream(added, cursor, max);
} else
added->extra_tlvs = NULL;
added->fail_immediate = fromwire_bool(cursor, max);
return added;
}
struct existing_htlc *fromwire_existing_htlc(const tal_t *ctx,
const u8 **cursor, size_t *max)
{
struct existing_htlc *existing = tal(ctx, struct existing_htlc);
existing->state = fromwire_u8(cursor, max);
existing->id = fromwire_u64(cursor, max);
existing->amount = fromwire_amount_msat(cursor, max);
fromwire_sha256(cursor, max, &existing->payment_hash);
existing->cltv_expiry = fromwire_u32(cursor, max);
fromwire(cursor, max, existing->onion_routing_packet,
sizeof(existing->onion_routing_packet));
if (fromwire_bool(cursor, max)) {
existing->payment_preimage = tal(existing, struct preimage);
fromwire_preimage(cursor, max, existing->payment_preimage);
} else
existing->payment_preimage = NULL;
if (fromwire_bool(cursor, max))
existing->failed = fromwire_failed_htlc(existing, cursor, max);
else
existing->failed = NULL;
if (fromwire_bool(cursor, max)) {
existing->path_key = tal(existing, struct pubkey);
fromwire_pubkey(cursor, max, existing->path_key);
} else
existing->path_key = NULL;
if (fromwire_bool(cursor, max)) {
existing->extra_tlvs = fromwire_len_and_tlvstream(existing, cursor, max);
} else
existing->extra_tlvs = NULL;
return existing;
}
void fromwire_fulfilled_htlc(const u8 **cursor, size_t *max,
struct fulfilled_htlc *fulfilled)
{
fulfilled->id = fromwire_u64(cursor, max);
fromwire_preimage(cursor, max, &fulfilled->payment_preimage);
}
struct failed_htlc *fromwire_failed_htlc(const tal_t *ctx, const u8 **cursor, size_t *max)
{
struct failed_htlc *failed = tal(ctx, struct failed_htlc);
enum onion_wire badonion;
failed->id = fromwire_u64(cursor, max);
badonion = fromwire_u16(cursor, max);
if (badonion) {
failed->onion = NULL;
if (!(badonion & BADONION))
return tal_free(failed);
failed->badonion = badonion;
failed->sha256_of_onion = tal(failed, struct sha256);
fromwire_sha256(cursor, max, failed->sha256_of_onion);
} else {
failed->sha256_of_onion = NULL;
failed->onion = fromwire_onionreply(failed, cursor, max);
}
return failed;
}
static enum htlc_state fromwire_htlc_state(const u8 **cursor, size_t *max)
{
enum htlc_state hstate = fromwire_u8(cursor, max);
if (hstate >= HTLC_STATE_INVALID) {
hstate = HTLC_STATE_INVALID;
fromwire_fail(cursor, max);
}
return hstate;
}
void fromwire_changed_htlc(const u8 **cursor, size_t *max,
struct changed_htlc *changed)
{
changed->newstate = fromwire_htlc_state(cursor, max);
changed->id = fromwire_u64(cursor, max);
}
enum side fromwire_side(const u8 **cursor, size_t *max)
{
u8 side = fromwire_u8(cursor, max);
if (side >= NUM_SIDES) {
side = NUM_SIDES;
fromwire_fail(cursor, max);
}
return side;
}
void fromwire_shachain(const u8 **cursor, size_t *max,
struct shachain *shachain)
{
size_t i;
shachain->min_index = fromwire_u64(cursor, max);
shachain->num_valid = fromwire_u32(cursor, max);
if (shachain->num_valid > ARRAY_SIZE(shachain->known)) {
fromwire_fail(cursor, max);
return;
}
for (i = 0; i < shachain->num_valid; i++) {
shachain->known[i].index = fromwire_u64(cursor, max);
fromwire_sha256(cursor, max, &shachain->known[i].hash);
}
}