From 8e0c19c76a359a03342cd864399481676d05ef90 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 18 Aug 2017 14:13:52 +0930 Subject: [PATCH] lightningd: add dev-fail command to inject permenant failure. A couple of double-free bugs founnd doing this, too. Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 39 ++++++++++++++++++++++++++++++++++++++- lightningd/subd.c | 5 +++++ tests/test_lightningd.py | 14 ++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 634073004..08364506c 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -59,6 +59,8 @@ static void reconnect_failed(struct lightningd_state *dstate, struct lightningd *ld = ld_from_dstate(dstate); struct peer *peer = peer_by_id(ld, connection_known_id(c)); + log_debug(peer->log, "reconnect_failed"); + tal_free(c); peer_reconnect(peer); } @@ -68,6 +70,8 @@ static void try_reconnect(struct peer *peer) struct connection *c; struct netaddr *addrs; + log_debug(peer->log, "try_reconnect: trying to reconnect"); + /* We may already be reconnected (another incoming connection) */ if (peer->owner) { log_debug(peer->log, "try_reconnect: already reconnected (%s)", @@ -109,7 +113,7 @@ void peer_fail_permanent(struct peer *peer, const u8 *msg) peer_state_name(peer->state), (int)tal_len(msg), (char *)msg); peer->error = towire_error(peer, &all_channels, msg); - peer->owner = NULL; + peer->owner = tal_free(peer->owner); if (taken(msg)) tal_free(msg); @@ -202,6 +206,7 @@ static void peer_start_closingd(struct peer *peer, static struct io_plan *send_error(struct io_conn *conn, struct peer_crypto_state *pcs) { + log_debug(pcs->peer->log, "Sending canned error"); return peer_write_message(conn, pcs, pcs->peer->error, (void *)io_close_cb); } @@ -720,6 +725,38 @@ static const struct json_command connect_command = { }; AUTODATA(json_command, &connect_command); +static void json_dev_fail(struct command *cmd, + const char *buffer, const jsmntok_t *params) +{ + struct lightningd *ld = ld_from_dstate(cmd->dstate); + jsmntok_t *peertok; + struct peer *peer; + + if (!json_get_params(buffer, params, + "id", &peertok, + NULL)) { + command_fail(cmd, "Need id"); + return; + } + + peer = peer_from_json(ld, buffer, peertok); + if (!peer) { + command_fail(cmd, "Could not find peer with that id"); + return; + } + + peer_internal_error(peer, "Failing due to dev-fail command"); + command_success(cmd, null_response(cmd)); +} + +static const struct json_command dev_fail_command = { + "dev-fail", + json_dev_fail, + "Fail with peer {id}", + "Returns {} on success" +}; +AUTODATA(json_command, &dev_fail_command); + struct log_info { enum log_level level; struct json_result *response; diff --git a/lightningd/subd.c b/lightningd/subd.c index 0fe43282e..535b2c025 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -371,6 +371,11 @@ static void destroy_subd(struct subd *sd) status = -1; break; } + + /* In case we're freed manually, such as peer_fail_permanent */ + if (sd->conn) + sd->conn = tal_free(sd->conn); + log_debug(sd->log, "finishing: %p", sd->finished); if (sd->finished) sd->finished(sd, status); diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index fd723825c..d1f3e614e 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -336,6 +336,20 @@ class LightningDTests(BaseLightningDTests): l2.daemon.wait_for_log('sendrawtx exit 0') assert l1.bitcoin.rpc.getmempoolinfo()['size'] == 1 + def test_permfail(self): + l1,l2 = self.connect() + + self.fund_channel(l1, l2, 10**6) + self.pay(l1,l2,200000000) + + # We fail l2, so l1 will reconnect to it. + l2.rpc.dev_fail(l1.info['id']); + l2.daemon.wait_for_log('Failing due to dev-fail command') + l2.daemon.wait_for_log('sendrawtx exit 0') + + # "Internal error" in hex + l1.daemon.wait_for_log('WIRE_ERROR.*496e7465726e616c206572726f72') + def test_gossip_jsonrpc(self): l1,l2 = self.connect()