From b19a4516d4f3cafe3e1f2b56c6ddee4ab1fe0314 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 6 Sep 2017 12:10:04 +0930 Subject: [PATCH] lightningd: fix crash when old openingd still around. We weren't killing it. Eventually it would die, and peer_owner_finished() would access subd->peer->owner, but that peer was freed already. Closes: #261 Reported-by: Christian Decker Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 4 +++- tests/test_lightningd.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index ecb0f1bed..1a0a96f2b 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -454,6 +454,7 @@ static bool peer_reconnected(struct lightningd *ld, int fd, const struct crypto_state *cs) { + struct subd *subd; struct peer *peer = peer_by_id(ld, id); if (!peer) return false; @@ -495,8 +496,9 @@ static bool peer_reconnected(struct lightningd *ld, case OPENINGD: /* Kill off openingd, forget old peer. */ + subd = peer->owner; peer->owner = NULL; /* We'll free it ourselves */ - tal_free(peer->owner); + tal_free(subd); tal_free(peer); /* A fresh start. */ diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index a800087f7..ce66dd1cf 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -816,6 +816,39 @@ class LightningDTests(BaseLightningDTests): l1.daemon.wait_for_log('-> CHANNELD_NORMAL') l2.daemon.wait_for_log('-> CHANNELD_NORMAL') + def test_reconnect_openingd(self): + # Openingd thinks we're still opening; funder reconnects.. + disconnects = ['0WIRE_ACCEPT_CHANNEL'] + l1 = self.node_factory.get_node() + l2 = self.node_factory.get_node(disconnect=disconnects) + l1.rpc.connect('localhost', l2.info['port'], l2.info['id']) + + addr = l1.rpc.newaddr()['address'] + txid = l1.bitcoin.rpc.sendtoaddress(addr, 20000 / 10**6) + tx = l1.bitcoin.rpc.getrawtransaction(txid) + l1.rpc.addfunds(tx) + + # It closes on us, we forget about it. + self.assertRaises(ValueError, l1.rpc.fundchannel, l2.info['id'], 20000) + assert l1.rpc.getpeer(l2.info['id']) == None + + # Reconnect. + l1.rpc.connect('localhost', l2.info['port'], l2.info['id']) + + # Truncate (hack to release old openingd). + with open(os.path.join(l2.daemon.lightning_dir, 'dev_disconnect'), "w"): + pass + + # We should get a message about old one exiting. + l2.daemon.wait_for_log('Subdaemon lightning_openingd died') + + # Should work fine. + l1.rpc.fundchannel(l2.info['id'], 20000) + l1.daemon.wait_for_log('sendrawtx exit 0') + + # Just to be sure, second openingd should die too. + l2.daemon.wait_for_log('Subdaemon lightning_openingd died \(0\)') + def test_reconnect_normal(self): # Should reconnect fine even if locked message gets lost. disconnects = ['-WIRE_FUNDING_LOCKED',