diff --git a/hsmd/hsm_wire.csv b/hsmd/hsm_wire.csv index 44a58b186..e21e629a0 100644 --- a/hsmd/hsm_wire.csv +++ b/hsmd/hsm_wire.csv @@ -176,6 +176,12 @@ hsm_get_per_commitment_point_reply,118 hsm_get_per_commitment_point_reply,,per_commitment_point,struct pubkey hsm_get_per_commitment_point_reply,,old_commitment_secret,?struct secret +# master -> hsmd: do you have a memleak? +hsm_dev_memleak,33 + +hsm_dev_memleak_reply,133 +hsm_dev_memleak_reply,,leak,bool + # channeld asks to check if claimed future commitment_secret is correct. hsm_check_future_secret,22 hsm_check_future_secret,,n,u64 diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 28a32b6f0..e1ac5d55b 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -101,7 +102,8 @@ static UINTMAP(struct client *) clients; static struct client *dbid_zero_clients[3]; static size_t num_dbid_zero_clients; -/*~ We need this deep inside bad_req_fmt, so we make it a global. */ +/*~ We need this deep inside bad_req_fmt, and for memleak, so we make it a + * global. */ static struct daemon_conn *status_conn; /* This is used for various assertions and error cases. */ @@ -1537,6 +1539,30 @@ static struct io_plan *handle_sign_node_announcement(struct io_conn *conn, return req_reply(conn, c, take(reply)); } +#if DEVELOPER +static struct io_plan *handle_memleak(struct io_conn *conn, + struct client *c, + const u8 *msg_in) +{ + struct htable *memtable; + bool found_leak; + u8 *reply; + + memtable = memleak_enter_allocations(tmpctx, msg_in, msg_in); + + /* Now delete clients and anything they point to. */ + memleak_remove_referenced(memtable, c); + memleak_scan_region(memtable, + dbid_zero_clients, sizeof(dbid_zero_clients)); + memleak_remove_uintmap(memtable, &clients); + memleak_scan_region(memtable, status_conn, tal_bytelen(status_conn)); + + found_leak = dump_memleak(memtable); + reply = towire_hsm_dev_memleak_reply(NULL, found_leak); + return req_reply(conn, c, take(reply)); +} +#endif /* DEVELOPER */ + /*~ This routine checks that a client is allowed to call the handler. */ static bool check_client_capabilities(struct client *client, enum hsm_wire_type t) @@ -1588,6 +1614,7 @@ static bool check_client_capabilities(struct client *client, case WIRE_HSM_SIGN_INVOICE: case WIRE_HSM_SIGN_COMMITMENT_TX: case WIRE_HSM_GET_CHANNEL_BASEPOINTS: + case WIRE_HSM_DEV_MEMLEAK: return (client->capabilities & HSM_CAP_MASTER) != 0; /*~ These are messages sent by the HSM so we should never receive them. @@ -1608,6 +1635,7 @@ static bool check_client_capabilities(struct client *client, case WIRE_HSM_GET_PER_COMMITMENT_POINT_REPLY: case WIRE_HSM_CHECK_FUTURE_SECRET_REPLY: case WIRE_HSM_GET_CHANNEL_BASEPOINTS_REPLY: + case WIRE_HSM_DEV_MEMLEAK_REPLY: break; } return false; @@ -1688,6 +1716,12 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSM_SIGN_MUTUAL_CLOSE_TX: return handle_sign_mutual_close_tx(conn, c, c->msg_in); +#if DEVELOPER + case WIRE_HSM_DEV_MEMLEAK: + return handle_memleak(conn, c, c->msg_in); +#else + case WIRE_HSM_DEV_MEMLEAK: +#endif /* DEVELOPER */ case WIRE_HSM_ECDH_RESP: case WIRE_HSM_CANNOUNCEMENT_SIG_REPLY: case WIRE_HSM_CUPDATE_SIG_REPLY: @@ -1703,6 +1737,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSM_GET_PER_COMMITMENT_POINT_REPLY: case WIRE_HSM_CHECK_FUTURE_SECRET_REPLY: case WIRE_HSM_GET_CHANNEL_BASEPOINTS_REPLY: + case WIRE_HSM_DEV_MEMLEAK_REPLY: break; } diff --git a/lightningd/memdump.c b/lightningd/memdump.c index 2f484fcc0..383123d7c 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -7,7 +7,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -16,6 +18,7 @@ #include #include #include +#include static void json_add_ptr(struct json_stream *response, const char *name, const void *ptr) @@ -228,11 +231,35 @@ static void connect_dev_memleak_done(struct subd *connectd, -1, 0, gossip_dev_memleak_done, cmd); } +static void hsm_dev_memleak_done(struct subd *hsmd, + const u8 *reply, + struct command *cmd) +{ + struct lightningd *ld = cmd->ld; + bool found_leak; + + if (!fromwire_hsm_dev_memleak_reply(reply, &found_leak)) { + command_fail(cmd, LIGHTNINGD, "Bad hsm_dev_memleak"); + return; + } + + if (found_leak) { + report_leak_info(cmd, hsmd); + return; + } + + /* No leak? Ask connectd. */ + subd_req(ld->connectd, ld->connectd, + take(towire_connect_dev_memleak(NULL)), + -1, 0, connect_dev_memleak_done, cmd); +} + static void json_memleak(struct command *cmd, const char *buffer UNNEEDED, const jsmntok_t *params UNNEEDED) { struct lightningd *ld = cmd->ld; + u8 *msg; if (!param(cmd, buffer, params, NULL)) return; @@ -243,10 +270,16 @@ static void json_memleak(struct command *cmd, return; } - subd_req(ld->connectd, ld->connectd, - take(towire_connect_dev_memleak(NULL)), - -1, 0, connect_dev_memleak_done, cmd); + /* For simplicity, we mark pending, though an error may complete it + * immediately. */ command_still_pending(cmd); + + /* We talk to hsm sync. */ + msg = towire_hsm_dev_memleak(NULL); + if (!wire_sync_write(ld->hsm_fd, take(msg))) + fatal("Could not write to HSM: %s", strerror(errno)); + + hsm_dev_memleak_done(ld->hsm, wire_sync_read(tmpctx, ld->hsm_fd), cmd); } static const struct json_command dev_memleak_command = {