lightningd: fix access to bcli plugin once it's freed.
What changed, and why it matters
This commit fixes a use-after-free bug: when the bitcoin plugin (bcli) was freed during shutdown, the gossip subsystem could still try to send it a request, reading from freed memory. The patch simply stops new plugin requests once the daemon is shutting down. It is a stability/crash bug rather than an obvious remote exploit, and the commit does not describe it as a security vulnerability.
Apply the patch. It is small, defensive, and prevents a shutdown-time use-after-free. No immediate incident response is warranted unless the crash can be shown to be remotely triggerable in a way that corrupts state or leaks sensitive data; current evidence only supports a local/DoS-style crash during shutdown.
Security signals we found
Use-after-free (UAF) on plugin object detected by Valgrind
Invalid read in strmap_add_ during plugin request dispatch
Crash-triggering condition tied to daemon shutdown lifecycle
No explicit security framing by vendor; treated as stability fix
Evidence from the diff
Valgrind caught an invalid read inside strmap_add_ via plugin_request_send -> bitcoin_plugin_send -> bitcoind_getrawblockbyheight_ -> bitcoind_getfilteredblock_ -> get_txout. The root cause is that get_txout can be triggered by a gossipd message after the bcli plugin has already been killed and freed (plugin_conn_finish -> plugin_kill -> tal_free). The fix adds an early return in get_txout when lightningd is in LD_STATE_SHUTDOWN, preventing any new async bitcoin plugin RPCs from being initiated after teardown begins.
Changed components
lightningd/gossip_control.cget_txout()bitcoind_getfilteredblock() / bitcoin_plugin_send()bcli plugin lifecycleInspect captured patch +4 / −0
diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c
index 0f013b76..7ecf0a53 100644
--- a/lightningd/gossip_control.c
+++ b/lightningd/gossip_control.c
@@ -102,6 +102,10 @@ static void get_txout(struct subd *gossip, const u8 *msg)
subd_send_msg(gossip, take(towire_gossipd_get_txout_reply(
NULL, scid, AMOUNT_SAT(0), NULL)));
} else {
+ /* If we're shutting down, don't ask plugins */
+ if (gossip->ld->state == LD_STATE_SHUTDOWN)
+ return;
+
/* Make a pointer of a copy of scid here, for got_filteredblock */
bitcoind_getfilteredblock(topo->bitcoind, topo->bitcoind,
short_channel_id_blocknum(scid),
Why this scored 34/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.