lightningd: handle amountless invoices in the onchain payment check
What changed, and why it matters
This fix repairs a crash in Core Lightning's invoice handling. When someone paid the on-chain fallback address of an invoice that accepted 'any' amount, the software dereferenced a NULL pointer and died with a fatal signal (segmentation fault). The patch simply skips the amount comparison when no specific amount was set, matching the behavior already used for regular Lightning payments.
Apply the patch. It is a minimal, targeted fix with an accompanying regression test. Nodes accepting on-chain fallback payments for amountless invoices should upgrade to avoid denial-of-service crashes.
Security signals we found
NULL-pointer dereference (SIGSEGV) in on-chain invoice payment validation
Daemon crash triggered by a normal, allowed invoice configuration (amount_msat='any')
Crash occurs during block tip processing, affecting node availability
Inconsistent validation: HTLC path already handled amountless invoices, on-chain path did not
Evidence from the diff
invoice_check_onchain_payment() in lightningd/invoice.c compared the received millisatoshi amount against *details->msat. For invoices created with amount_msat=’any’, details->msat is NULL, causing a NULL-pointer dereference and SIGSEGV during block processing (filter_block_txs -> add_tip -> get_new_block). The fix adds a NULL guard: only perform the underpayment check when details->msat is non-NULL. A previously xfail test is now enabled and no longer expects daemon death.
Changed components
lightningd/invoice.clightningd/chaintopology.c (call site)tests/test_invoices.pyInspect captured patch +3 / −6
### lightningd/invoice.c
@@ -971,7 +971,8 @@ void invoice_check_onchain_payment(struct lightningd *ld,
details = invoices_get_details(tmpctx, ld->wallet->invoices, inv_dbid);
- if (amount_msat_less(msat, *details->msat)) {
+ /* details->msat is NULL if they specified "any": any amount will do. */
+ if (details->msat && amount_msat_less(msat, *details->msat)) {
// notify_underpaid_onchain_invoice();
return;
}
### tests/test_invoices.py
@@ -934,13 +934,9 @@ def on_payment(payment, plugin, **kwargs):
assert l1.rpc.listinvoices("inv1") == {"invoices": []}
-@pytest.mark.xfail(strict=True)
def test_unified_invoices_any_amount(node_factory, bitcoind):
"""An onchain payment to the fallback of an amountless invoice must not crash us"""
- # We expect the daemon to die (and log a fatal signal) until this is fixed.
- l1 = node_factory.get_node(options={'invoices-onchain-fallback': None},
- may_fail=True,
- broken_log=r'FATAL SIGNAL')
+ l1 = node_factory.get_node(options={'invoices-onchain-fallback': None})
amount_sat = 1000
inv = l1.rpc.invoice('any', "inv1", "test_unified_invoices_any_amount")Why this scored 69/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.