[PATCH 1/1] transport-helper, connect: add atexit handler to reap children on abnormal exit
From: Andrew Au <hidden>
Date: 2026-02-23 16:51:56
Subsystem:
the rest · Maintainer:
Linus Torvalds
From: Andrew Au <redacted> When git exits via exit(128) on transport errors, child processes (git-remote-https, ssh, proxy) are never waited on because the normal cleanup paths (disconnect_helper, finish_connect) are bypassed. When git is PID 1 in a container, these un-reaped children become zombies. Register atexit handlers in both transport-helper.c and connect.c to ensure children are reaped on any exit path. Clear the handlers on the normal cleanup paths to avoid double-waiting. Signed-off-by: Andrew Au <redacted> --- connect.c | 17 +++++++++++++++++ transport-helper.c | 11 +++++++++++ 2 files changed, 28 insertions(+)
diff --git a/connect.c b/connect.c
index eef752f14..322b1f816 100644
--- a/connect.c
+++ b/connect.c@@ -20,6 +20,18 @@ static char *server_capabilities_v1; static struct strvec server_capabilities_v2 = STRVEC_INIT; static const char *next_server_feature_value(const char *feature, int *len, int *offset); +/* + * Ensure the connection child (ssh, proxy, or local git) is reaped on + * any exit path, mirroring the transport-helper.c atexit pattern. + */ +static struct child_process *conn_to_reap; + +static void cleanup_conn_on_exit(void) +{ + if (conn_to_reap) + finish_command(conn_to_reap); +} + static int check_ref(const char *name, unsigned int flags) { if (!flags)
@@ -991,6 +1003,8 @@ static struct child_process *git_proxy_connect(int fd[2], char *host) proxy->out = -1; if (start_command(proxy)) die(_("cannot start proxy %s"), git_proxy_command); + conn_to_reap = proxy; + atexit(cleanup_conn_on_exit); fd[0] = proxy->out; /* read from proxy stdout */ fd[1] = proxy->in; /* write to proxy stdin */ return proxy;
@@ -1449,6 +1463,8 @@ struct child_process *git_connect(int fd[2], const char *url, if (start_command(conn)) die(_("unable to fork")); + conn_to_reap = conn; + atexit(cleanup_conn_on_exit); fd[0] = conn->out; /* read from child's stdout */ fd[1] = conn->in; /* write to child's stdin */
@@ -1466,6 +1482,7 @@ int finish_connect(struct child_process *conn) return 0; code = finish_command(conn); + conn_to_reap = NULL; free(conn); return code; }
diff --git a/transport-helper.c b/transport-helper.c
index e95267a4a..cdfd40dfc 100644
--- a/transport-helper.c
+++ b/transport-helper.c@@ -17,6 +17,14 @@ static int debug; +static struct child_process *helper_to_reap; + +static void cleanup_helper_on_exit(void) +{ + if (helper_to_reap) + finish_command(helper_to_reap); +} + struct helper_data { const char *name; struct child_process *helper;
@@ -147,6 +155,8 @@ static struct child_process *get_helper(struct transport *transport) exit(code); data->helper = helper; + helper_to_reap = helper; + atexit(cleanup_helper_on_exit); data->no_disconnect_req = 0; refspec_init(&data->rs, REFSPEC_FETCH);
@@ -249,6 +259,7 @@ static int disconnect_helper(struct transport *transport) close(data->helper->out); fclose(data->out); res = finish_command(data->helper); + helper_to_reap = NULL; FREE_AND_NULL(data->helper); } return res;
--
2.43.0