pytest: reproduce crash when node disconnects between hooks:
What changed, and why it matters
This commit adds a new test that reproduces a crash in Core Lightning. The crash happens when a peer disconnects while the lightning node is still waiting for a plugin's 'peer_connected' hook to finish. The test is currently marked as expected to fail, meaning the underlying bug has not been fixed yet in this commit. It is a reproduction test, not a fix.
Treat this as a known unfixed crash bug. A developer should fix the null-deref/use-after-free in peer_connected_serialize before the affected code reaches a release. Network operators should avoid chaining slow peer_connected hook plugins or exposing nodes to untrusted peers that can disconnect at will until a fix is available.
Security signals we found
NULL pointer dereference or use-after-free in peer_connected_serialize when peer disconnects during chained peer_connected hooks
Daemon crash (SIGSEGV / FATAL SIGNAL 11) in peer_control.c
Race condition between peer disconnection and plugin hook serialization
Test-only commit: no production code change, only reproduction
Evidence from the diff
The commit introduces a pytest test, test_plugin_connected_hook_disconnect_crash, and modifies a test plugin (peer_connected_logger_a.py) to optionally sleep until a file appears. The test sets up a node with two peer_connected hook plugins, connects a peer, makes the first plugin block, disconnects the peer by stopping it, then unblocks the first plugin. The expected outcome is a crash (SIGSEGV) in peer_connected_serialize in lightningd/peer_control.c when the second hook is invoked after the peer has disconnected. The test is marked xfail(strict=True), confirming it reproduces an unfixed bug.
Changed components
lightningd/peer_control.clightningd/plugin_hook.ctests/test_plugin.pytests/plugins/peer_connected_logger_a.pyInspect captured patch +31 / −0
diff --git a/tests/plugins/peer_connected_logger_a.py b/tests/plugins/peer_connected_logger_a.py
index 5ded2306..484bb5f2 100755
--- a/tests/plugins/peer_connected_logger_a.py
+++ b/tests/plugins/peer_connected_logger_a.py
@@ -4,6 +4,8 @@
"""
from pyln.client import Plugin
+import os
+import time
plugin = Plugin()
@@ -11,7 +13,12 @@ plugin = Plugin()
@plugin.hook('peer_connected')
def on_connected(peer, plugin, **kwargs):
print(f"peer_connected_logger_a {peer['id']} {peer}")
+ if plugin.get_option("logger_a_sleep") is True:
+ # Block until file appears
+ while not os.path.exists("unsleep"):
+ time.sleep(0.25)
return {'result': 'continue'}
+plugin.add_option("logger_a_sleep", False, 'Block until unsleep file exists', opt_type='bool')
plugin.run()
diff --git a/tests/test_plugin.py b/tests/test_plugin.py
index 12a9d6e5..b40cb515 100644
--- a/tests/test_plugin.py
+++ b/tests/test_plugin.py
@@ -514,6 +514,30 @@ def test_plugin_connected_hook_chaining(node_factory):
assert not l1.daemon.is_in_log(f"peer_connected_logger_b {l3id}")
+@pytest.mark.xfail(strict=True)
+def test_plugin_connected_hook_disconnect_crash(node_factory, executor):
+ """A peer disconnnects between plugin hook invocations"""
+ opts = [{},
+ {'plugin':
+ [os.path.join(os.getcwd(),
+ 'tests/plugins/peer_connected_logger_a.py'),
+ os.path.join(os.getcwd(),
+ 'tests/plugins/peer_connected_logger_b.py')],
+ 'logger_a_sleep': True},
+ ]
+
+ l1, l2 = node_factory.get_nodes(2, opts=opts)
+ executor.submit(l1.rpc.connect, l2.info['id'], 'localhost', l2.port)
+ l2.daemon.wait_for_log(f'plugin-peer_connected_logger_a.py: peer_connected_logger_a {l1.info["id"]}')
+ l1.stop()
+
+ # Now make first plugin continue...
+ open(os.path.join(l2.daemon.lightning_dir, TEST_NETWORK, "unsleep"), "w").close()
+
+ # Should get log from second
+ l2.daemon.wait_for_log(f'plugin-peer_connected_logger_b.py: peer_connected_logger_b {l1.info["id"]}')
+
+
def test_peer_connected_remote_addr(node_factory):
"""This tests the optional tlv `remote_addr` being passed to a plugin.
Why this scored 59/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.