From 0003922172c5c197865f81c73d2cd9affb945b20 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Thu, 13 Aug 2026 11:42:41 +0100 Subject: [PATCH] lightningd: check rpc names collisions with builtin commands We crashed if the name collision happened to be a builtin command. ``` lightningd: FATAL SIGNAL 11 (version v26.06-241-gb35b848-modded) 0x5573cd532b2c send_backtrace common/daemon.c:38 0x5573cd532bb6 crashdump common/daemon.c:83 0x7fe1fd4ccdef ??? ./signal/../sysdeps/unix/sysv/linux/x86_64/libc_sigaction.c:0 0x5573cd4d51a4 plugin_rpcmethod_add lightningd/plugin.c:1396 0x5573cd4d5268 plugin_rpcmethods_add lightningd/plugin.c:1423 0x5573cd4d57ef plugin_parse_getmanifest_response lightningd/plugin.c:1805 0x5573cd4d68db plugin_manifest_cb lightningd/plugin.c:1827 0x5573cd4d2316 plugin_response_handle lightningd/plugin.c:692 0x5573cd4d7443 plugin_read_json lightningd/plugin.c:781 0x5573cd56fd01 next_plan ccan/ccan/io/io.c:60 0x5573cd57018c do_plan ccan/ccan/io/io.c:422 0x5573cd570245 io_ready ccan/ccan/io/io.c:439 0x5573cd571be3 io_loop ccan/ccan/io/poll.c:471 0x5573cd4a5a99 io_loop_with_timers lightningd/io_loop_with_timers.c:22 0x5573cd4d61d1 plugins_init lightningd/plugin.c:2063 0x5573cd4aad6c main lightningd/lightningd.c:1269 0x7fe1fd4b6ca7 __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 0x7fe1fd4b6d64 __libc_start_main_impl ../csu/libc-start.c:360 0x5573cd47a120 ??? _start+0x20:0 0xffffffffffffffff ??? ???:0 ``` Changelog-Fixed: lightningd: checks for rpc name collisions with builtin commands before registering plugin Reported-by: Vincenzo Palazzo (Bitcoin Security Council finding 2026-08-11) Signed-off-by: Lagrang3 --- lightningd/plugin.c | 17 ++++++++++++----- tests/test_plugin.py | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 036903e91dae..deb29c4ce02a 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -1393,11 +1393,18 @@ static const char *plugin_rpcmethod_add(struct plugin *plugin, if (!jsonrpc_command_add(plugin->plugins->ld->jsonrpc, cmd, usage)) { struct plugin *p = find_plugin_for_command(plugin->plugins->ld, cmd->name); - return tal_fmt( - plugin, - "Could not register method \"%s\", a method with " - "that name is already registered by plugin %s", - cmd->name, p->cmd); + if (p) + return tal_fmt( + plugin, + "Could not register method \"%s\", a method with " + "that name is already registered by plugin %s", + cmd->name, p->cmd); + else + return tal_fmt(plugin, + "Could not register method \"%s\", a " + "builtin method with " + "that name is already registered", + cmd->name); } tal_arr_expand(&plugin->methods, cmd->name); return NULL; diff --git a/tests/test_plugin.py b/tests/test_plugin.py index bb4604aaf33c..65d145b19afa 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -6134,3 +6134,21 @@ def test_bwatch_blockdepth_watch_no_fire_before_start_block(node_factory, bitcoi # Clean up l1.rpc.delblockdepthwatch(owner=owner, start_block=future_start) + + +def test_command_collision(node_factory): + def xpay_duplicate(plugin): + @plugin.method("xpay") + def on_xpay(plugin): + return {} + l1 = node_factory.get_node(inline_plugin=xpay_duplicate) + l1.daemon.logsearch_start = 0 + l1.daemon.wait_for_log("a method with that name is already registered by plugin") + + def builtin_duplicate(plugin): + @plugin.method("getinfo") + def on_getinfo(plugin): + return {} + l2 = node_factory.get_node(inline_plugin=builtin_duplicate) + l2.daemon.logsearch_start = 0 + l2.daemon.wait_for_log("a builtin method with that name is already registered")