libplugin: use jsonrpc_io for stdin from lightningd.
What changed, and why it matters
This commit refactors how Core Lightning plugins read JSON commands from the main lightningd process. It replaces a hand-rolled JSON buffering and parsing loop with a shared helper called jsonrpc_io. The change is described by the author as a cleanup and efficiency improvement, not a security fix. There is no direct evidence in the commit or supplied references that this resolves a vulnerability.
No immediate security action required. Treat as routine refactoring. If reviewing for security, verify that jsonrpc_io correctly handles malformed, truncated, or oversized JSON input and that error paths terminate the plugin safely.
Security signals we found
Refactor of JSON input parsing path for plugin stdin
Removal of manual buffer resize and memmove logic
Adoption of shared jsonrpc_io abstraction already used elsewhere
No mention of security, vulnerability, CVE, or bug fix in commit message
Evidence from the diff
The patch in plugins/libplugin.c removes plugin-managed buffer, used, len_read, jsmn_parser, and jsmntok_t fields for stdin from lightningd, and instead uses a struct jsonrpc_io object with jsonrpc_io_new, jsonrpc_io_read, jsonrpc_io_parse, and jsonrpc_io_parse_done. The previous ld_read_json_one/ld_read_json logic that grew a tal buffer, parsed incrementally, and memmoved consumed data is replaced by the shared jsonrpc_io loop. The same jsonrpc_io abstraction is already used for async RPC input (p->jsonrpc_in). No bounds-checking, input-validation, or memory-safety bugs are explicitly addressed, and the commit message frames the change as efficiency/cleanup.
Changed components
plugins/libplugin.cPlugin stdin JSON-RPC input handlingjsonrpc_io abstractionInspect captured patch +22 / −65
diff --git a/plugins/libplugin.c b/plugins/libplugin.c
index 8e1fbc95..166d0404 100644
--- a/plugins/libplugin.c
+++ b/plugins/libplugin.c
@@ -88,10 +88,7 @@ struct plugin {
const char **beglist;
/* To read from lightningd */
- char *buffer;
- size_t used, len_read;
- jsmn_parser parser;
- jsmntok_t *toks;
+ struct jsonrpc_io *lightningd_in;
/* To write to lightningd */
struct list_head js_list;
@@ -2222,65 +2219,31 @@ static void ld_command_handle(struct plugin *plugin,
plugin_err(plugin, "Unknown command '%s'", cmd->methodname);
}
-/**
- * Try to parse a complete message from lightningd's buffer, and return true
- * if we could handle it.
- */
-static bool ld_read_json_one(struct plugin *plugin)
+static struct io_plan *ld_read_json(struct io_conn *conn,
+ struct plugin *plugin)
{
- bool complete;
+ /* Gather an parse any new bytes */
+ for (;;) {
+ const jsmntok_t *toks;
+ const char *buf;
+ const char *err;
- if (!json_parse_input(&plugin->parser, &plugin->toks,
- plugin->buffer, plugin->used,
- &complete)) {
- plugin_err(plugin, "Failed to parse JSON response '%.*s'",
- (int)plugin->used, plugin->buffer);
- return false;
- }
+ err = jsonrpc_io_parse(tmpctx,
+ plugin->lightningd_in,
+ &toks, &buf);
+ if (err)
+ plugin_err(plugin, "%s", err);
- if (!complete) {
- /* We need more. */
- return false;
- }
+ if (!toks)
+ break;
- /* Empty buffer? (eg. just whitespace). */
- if (tal_count(plugin->toks) == 1) {
- toks_reset(plugin->toks);
- jsmn_init(&plugin->parser);
- plugin->used = 0;
- return false;
+ ld_command_handle(plugin, buf, toks);
+ jsonrpc_io_parse_done(plugin->lightningd_in);
}
- /* FIXME: Spark doesn't create proper jsonrpc 2.0! So we don't
- * check for "jsonrpc" here. */
- ld_command_handle(plugin, plugin->buffer, plugin->toks);
-
- /* Move this object out of the buffer */
- memmove(plugin->buffer, plugin->buffer + plugin->toks[0].end,
- tal_count(plugin->buffer) - plugin->toks[0].end);
- plugin->used -= plugin->toks[0].end;
- toks_reset(plugin->toks);
- jsmn_init(&plugin->parser);
-
- return true;
-}
-
-static struct io_plan *ld_read_json(struct io_conn *conn,
- struct plugin *plugin)
-{
- plugin->used += plugin->len_read;
- if (plugin->used && plugin->used == tal_count(plugin->buffer))
- tal_resize(&plugin->buffer, plugin->used * 2);
-
- /* Read and process all messages from the connection */
- while (ld_read_json_one(plugin))
- ;
-
- /* Now read more from the connection */
- return io_read_partial(plugin->stdin_conn,
- plugin->buffer + plugin->used,
- tal_count(plugin->buffer) - plugin->used,
- &plugin->len_read, ld_read_json, plugin);
+ /* Read more */
+ return jsonrpc_io_read(conn, plugin->lightningd_in,
+ ld_read_json, plugin);
}
static struct io_plan *ld_write_json(struct io_conn *conn,
@@ -2326,9 +2289,7 @@ static struct io_plan *stdin_conn_init(struct io_conn *conn,
{
plugin->stdin_conn = conn;
io_set_finish(conn, ld_conn_finish, plugin);
- return io_read_partial(plugin->stdin_conn, plugin->buffer,
- tal_bytelen(plugin->buffer), &plugin->len_read,
- ld_read_json, plugin);
+ return ld_read_json(conn, plugin);
}
/* lightningd reads from our stdout */
@@ -2369,12 +2330,8 @@ static struct plugin *new_plugin(const tal_t *ctx,
p->id = name;
p->developer = developer;
p->deprecated_ok_override = NULL;
- p->buffer = tal_arr(p, char, 64);
+ p->lightningd_in = jsonrpc_io_new(p);
list_head_init(&p->js_list);
- p->used = 0;
- p->len_read = 0;
- jsmn_init(&p->parser);
- p->toks = toks_alloc(p);
/* Async RPC */
p->jsonrpc_in = jsonrpc_io_new(p);
list_head_init(&p->rpc_js_list);
Why this scored 11/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.