lightningd: don't process more than 100 commands from a plugin at once.
What changed, and why it matters
This change adds a limit so that Core Lightning processes at most 100 plugin responses in one go before yielding control back to the main event loop. The stated goal is to prevent a misbehaving or malicious plugin from flooding the daemon with responses and starving other work. It is framed by the author as a hardening measure rather than a fix for a known exploitable bug.
Treat as a low-risk hardening improvement. No immediate action required unless running a version prior to this commit and concerned about plugin-induced event-loop starvation; in that case, upgrade. Monitor for follow-up disclosures from the project or researchers that might reclassify the issue.
Security signals we found
Denial-of-service hardening: limits work done per plugin callback to avoid event-loop starvation
Use of io_always() to explicitly yield after a bounded number of responses
Author describes change as preventive ('don't let it flood us') rather than a fix for a reported vulnerability
Evidence from the diff
In lightningd/plugin.c, plugin_read_json() now counts parsed responses and, after 100 responses in a single invocation, returns io_always() to reschedule itself. This prevents a plugin from causing long-running JSON parsing/response handling that would monopolize the ccan/io event loop. The commit message explicitly says the change is precautionary and notes that performance is now fast enough that the existing test no longer shows an effect.
Changed components
lightningd/plugin.cplugin JSON response handling loopInspect captured patch +7 / −0
diff --git a/lightningd/plugin.c b/lightningd/plugin.c
index 54d4b060..f4a2f1a6 100644
--- a/lightningd/plugin.c
+++ b/lightningd/plugin.c
@@ -710,6 +710,7 @@ static struct io_plan *plugin_read_json(struct io_conn *conn,
const char *new_bytes, *buffer;
const jsmntok_t *toks;
size_t new_bytes_len;
+ size_t num_responses = 0;
/* wallet is NULL in really early code */
bool want_transaction = (plugin->plugins->want_db_transaction
&& wallet != NULL);
@@ -803,6 +804,12 @@ static struct io_plan *plugin_read_json(struct io_conn *conn,
}
jsonrpc_io_parse_done(plugin->json_in);
+ /* Don't let it flood us with logs/responses and starve everyone else */
+ if (num_responses++ == 100) {
+ log_debug(plugin->log, "Pausing response parsing after %zu response", num_responses);
+ /* Call us back, as if we read nothing new */
+ return io_always(conn, plugin_read_json, plugin);
+ }
}
/* Now read more from the connection */
Why this scored 36/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.