connectd: don't complain if lightningd is unresponsive while doing dev-memleak.
What changed, and why it matters
This is a developer-only fix for a flaky test. The `dev-memleak` diagnostic tool can take so long that another component (connectd) thought the main daemon had hung, logging a scary but harmless 'BROKEN' message that broke automated tests. The patch makes connectd suppress that complaint while `dev-memleak` is running. It is not a security vulnerability fix and does not change normal node behavior.
No security action required. Treat as a normal test-flakiness / developer-tool improvement.
Security signals we found
No security-relevant code path modified
Change is gated behind developer-only `dev-memleak` command
Only effect is suppression of an internal diagnostic warning
No input validation, memory safety, or privilege boundary changes
Evidence from the diff
The commit addresses a false-positive ‘BROKEN’ log from connectd when lightningd is slow during the dev-memleak memory-leak scan. It adds a dev_lightningd_is_slow flag in connectd, set by dev_connect_memleak(), and skips the >5000ms wake-delay warning in write_to_subd() when the flag is true. It also reorders json_memleak() so connectd is queried first, giving it the side-effect of setting that flag before the synchronous lightningd and hsmd scans run. No cryptographic, network, or consensus code is changed.
Changed components
connectd/connectd.cconnectd/connectd.hconnectd/multiplex.clightningd/memdump.cInspect captured patch +45 / −35
diff --git a/connectd/connectd.c b/connectd/connectd.c
index f1c9a1b7..90cbc30c 100644
--- a/connectd/connectd.c
+++ b/connectd/connectd.c
@@ -2069,6 +2069,10 @@ static void dev_connect_memleak(struct daemon *daemon, const u8 *msg)
struct htable *memtable;
bool found_leak;
+ /* As a side-effect, this tells us lightningd will be unresponsive,
+ * so don't complain (and break CI!) if it's slow. */
+ daemon->dev_lightningd_is_slow = true;
+
memtable = memleak_start(tmpctx);
memleak_ptr(memtable, msg);
@@ -2521,6 +2525,7 @@ int main(int argc, char *argv[])
daemon->dev_suppress_gossip = false;
daemon->custom_msgs = NULL;
daemon->dev_exhausted_fds = false;
+ daemon->dev_lightningd_is_slow = false;
/* We generally allow 1MB per second per peer, except for dev testing */
daemon->gossip_stream_limit = 1000000;
daemon->scid_htable = new_htable(daemon, scid_htable);
diff --git a/connectd/connectd.h b/connectd/connectd.h
index cae8b149..1e78834b 100644
--- a/connectd/connectd.h
+++ b/connectd/connectd.h
@@ -370,6 +370,8 @@ struct daemon {
bool dev_no_reconnect;
/* --dev-fast-reconnect */
bool dev_fast_reconnect;
+ /* Don't complain about lightningd being unresponsive. */
+ bool dev_lightningd_is_slow;
};
/* Called by io_tor_connect once it has a connection out. */
diff --git a/connectd/multiplex.c b/connectd/multiplex.c
index 5c6fc4fe..31f71bb9 100644
--- a/connectd/multiplex.c
+++ b/connectd/multiplex.c
@@ -1155,7 +1155,7 @@ static struct io_plan *write_to_subd(struct io_conn *subd_conn,
if (subd->peer->peer_in_lastmsg != -1) {
u64 msec = time_to_msec(timemono_between(time_mono(),
subd->peer->peer_in_lasttime));
- if (msec > 5000)
+ if (msec > 5000 && !subd->peer->daemon->dev_lightningd_is_slow)
status_peer_broken(&subd->peer->id,
"wake delay for %s: %"PRIu64"msec",
peer_wire_name(subd->peer->peer_in_lastmsg),
diff --git a/lightningd/memdump.c b/lightningd/memdump.c
index ec0c49ca..cbd1b31d 100644
--- a/lightningd/memdump.c
+++ b/lightningd/memdump.c
@@ -83,6 +83,7 @@ static const struct json_command dev_memdump_command = {
};
AUTODATA(json_command, &dev_memdump_command);
+
static void memleak_log(struct logger *log, const char *fmt, ...)
{
va_list ap;
@@ -91,9 +92,29 @@ static void memleak_log(struct logger *log, const char *fmt, ...)
va_end(ap);
}
-static void finish_report(const struct leak_detect *leaks)
+static bool lightningd_check_leaks(struct command *cmd)
{
+ struct lightningd *ld = cmd->ld;
+ struct htable *memtable;
+
+ /* Enter everything, except this cmd and its jcon */
+ memtable = memleak_start(cmd);
+
+ /* This command is not a leak! */
+ memleak_ptr(memtable, cmd);
+ memleak_ignore_children(memtable, cmd);
+
+ /* Now delete ld and those which it has pointers to. */
+ memleak_scan_obj(memtable, ld);
+
+ return dump_memleak(memtable, memleak_log, ld->log);
+}
+
+static void finish_report(struct leak_detect *leaks)
+{
+ bool found_leak;
struct json_stream *response;
+ const u8 *msg;
/* If it timed out, we free ourselved and exit! */
if (!leaks->cmd) {
@@ -101,6 +122,18 @@ static void finish_report(const struct leak_detect *leaks)
return;
}
+ /* Check for our own leaks. */
+ if (lightningd_check_leaks(leaks->cmd))
+ tal_arr_expand(&leaks->leakers, "lightningd");
+
+ /* Check hsmd for leaks. */
+ msg = hsm_sync_req(tmpctx, leaks->cmd->ld, take(towire_hsmd_dev_memleak(NULL)));
+ if (!fromwire_hsmd_dev_memleak_reply(msg, &found_leak))
+ fatal("Bad HSMD_DEV_MEMLEAK_REPLY: %s", tal_hex(tmpctx, msg));
+
+ if (found_leak)
+ report_subd_memleak(leaks, leaks->cmd->ld->hsm);
+
response = json_stream_success(leaks->cmd);
json_array_start(response, "leaks");
for (size_t num_leakers = 0;
@@ -177,32 +210,12 @@ static void connect_dev_memleak_done(struct subd *connectd,
report_subd_memleak(leaks, connectd);
}
-static bool lightningd_check_leaks(struct command *cmd)
-{
- struct lightningd *ld = cmd->ld;
- struct htable *memtable;
-
- /* Enter everything, except this cmd and its jcon */
- memtable = memleak_start(cmd);
-
- /* This command is not a leak! */
- memleak_ptr(memtable, cmd);
- memleak_ignore_children(memtable, cmd);
-
- /* Now delete ld and those which it has pointers to. */
- memleak_scan_obj(memtable, ld);
-
- return dump_memleak(memtable, memleak_log, ld->log);
-}
-
static struct command_result *json_memleak(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct lightningd *ld = cmd->ld;
- const u8 *msg;
- bool found_leak;
struct leak_detect *leaks;
if (!param_check(cmd, buffer, params, NULL))
@@ -221,19 +234,9 @@ static struct command_result *json_memleak(struct command *cmd,
leaks->num_outstanding_requests = 0;
leaks->leakers = tal_arr(leaks, const char *, 0);
- /* Check for our own leaks. */
- if (lightningd_check_leaks(cmd))
- tal_arr_expand(&leaks->leakers, "lightningd");
-
- /* hsmd is sync, so do that first. */
- msg = hsm_sync_req(tmpctx, cmd->ld, take(towire_hsmd_dev_memleak(NULL)));
- if (!fromwire_hsmd_dev_memleak_reply(msg, &found_leak))
- fatal("Bad HSMD_DEV_MEMLEAK_REPLY: %s", tal_hex(tmpctx, msg));
-
- if (found_leak)
- report_subd_memleak(leaks, ld->hsm);
-
- /* Now do all the async ones. */
+ /* Now do all the async ones. By doing connectd first, it
+ * has the side-effect of suppressing the complaint it makes
+ * about us being unresponsive. */
start_leak_request(subd_req(ld->connectd, ld->connectd,
take(towire_connectd_dev_memleak(NULL)),
-1, 0, connect_dev_memleak_done, leaks),
Why this scored 18/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.