lightningd: don't process more than 100 commands from a JSONRPC at once.
What changed, and why it matters
This change tweaks how Core Lightning handles incoming JSON-RPC commands so it never processes more than 100 commands or 250 milliseconds in one go without taking a break to let other connections and tasks run. The goal is to prevent a single busy connection from starving the rest of the node. The commit itself frames it as a hardening improvement rather than a fix for a known active bug or vulnerability.
Treat as a routine hardening patch. Review whether local RPC access controls and authentication already limit exposure, since the issue only matters if untrusted or compromised clients can send many RPC commands. No urgent security response is indicated by the commit itself.
Security signals we found
Resource exhaustion / fairness hardening: limits per-connection JSON-RPC command processing before yielding to the event loop
Potential denial-of-service vector: a single RPC client could previously monopolize daemon CPU/I/O time with many pipelined requests
No explicit bug, CVE, or exploit mentioned in commit or supplied references
Evidence from the diff
The patch modifies read_json() in lightningd/jsonrpc.c to count parsed commands (num_parsed) and yield via io_always() after 100 commands or 250 ms, regardless of whether db_batching is enabled. Previously, the yield-on-timeout logic only ran when jcon->db_batching was true. The change also moves the transaction commit before the yield. This is a fairness/scheduling improvement: a single JSON-RPC connection sending many batched or pipelined requests could previously keep the daemon in read_json for an extended period, delaying other I/O and RPC clients. The commit message says it is precautionary and does not trigger on their benchmark.
Changed components
lightningd/jsonrpc.cJSON-RPC command ingestion pathDatabase transaction batching / commit behaviorInspect captured patch +12 / −11
diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c
index 48f5cd75..2b1f14a3 100644
--- a/lightningd/jsonrpc.c
+++ b/lightningd/jsonrpc.c
@@ -1190,6 +1190,7 @@ static struct io_plan *read_json(struct io_conn *conn,
size_t len_read;
const jsmntok_t *toks;
const char *buffer, *error;
+ size_t num_parsed = 0;
buffer = jsonrpc_newly_read(jcon->json_in, &len_read);
if (len_read)
@@ -1219,20 +1220,20 @@ again:
parse_request(jcon, buffer, toks);
jsonrpc_io_parse_done(jcon->json_in);
+ /* Don't ever process for more than 100 commands or 250 msec
+ * without giving others a chance */
+ if (num_parsed++ == 100
+ || time_greater(timemono_between(time_mono(), start_time),
+ time_from_msec(250))) {
+ db_commit_transaction(jcon->ld->wallet->db);
+ log_debug(jcon->log, "Pausing parsing after %zu requests", num_parsed);
+ /* Call us back, as if we read nothing new */
+ return io_always(conn, read_json, jcon);
+ }
+
if (!jcon->db_batching) {
db_commit_transaction(jcon->ld->wallet->db);
in_transaction = false;
- } else {
- /* FIXME: io_always() should interleave with
- * real IO, and then we should rotate order we
- * service fds in, to avoid starvation. */
- if (time_greater(timemono_between(time_mono(),
- start_time),
- time_from_msec(250))) {
- db_commit_transaction(jcon->ld->wallet->db);
- /* Call us back, as if we read nothing new */
- return io_always(conn, read_json, jcon);
- }
}
goto again;
Why this scored 37/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.