From f7dd0caa2f5c1e27b20bc19bdb0ba7b6338b727f Mon Sep 17 00:00:00 2001 From: Ankit Mahajan Date: Tue, 16 Jun 2026 08:42:32 +0530 Subject: [PATCH 1/3] tty: Fix file descriptor leak in pty_create_ptmx_index() In pty_create_ptmx_index(), when fdstore_add(fd) fails, the function returns -1 without closing the file descriptor obtained from __pty_open_ptmx_index(). This leaks the ptmx fd. Since fdstore_add() internally duplicates the descriptor, move the close(fd) before the error check so the fd is always closed regardless of whether fdstore_add succeeded or failed. Assisted-by: Cursor:claude-4.6-sonnet Signed-off-by: Ankit Mahajan --- criu/tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criu/tty.c b/criu/tty.c index 9a4520d53..15ce94028 100644 --- a/criu/tty.c +++ b/criu/tty.c @@ -2345,9 +2345,9 @@ static int pty_create_ptmx_index(int dfd, int index, int flags) return -1; id = fdstore_add(fd); + close(fd); if (id < 0) return -1; - close(fd); list_for_each_entry(info, &all_ttys, list) { if (!is_pty(info->driver)) From d6895686ff939467ae5eba892f22ea6d6a3aa607 Mon Sep 17 00:00:00 2001 From: Ankit Mahajan Date: Tue, 16 Jun 2026 08:43:02 +0530 Subject: [PATCH 2/3] sk-unix: Fix socket descriptor leaks on error paths Fix several file descriptor leaks in Unix socket restore functions: 1. open_unixsk_pair_master(): After socketpair() creates sk[0] and sk[1], multiple error paths return -1 without closing the socket descriptors. Replace bare returns with goto labels that ensure proper cleanup. Use err_pair when both sockets are still open, and err when only sk[0] needs closing (after sk[1] is consumed by setup_and_serve_out). 2. open_unixsk_standalone(): In the USK_SERVICE branch, if send_criu_dump_resp() fails, both sks[0] and sks[1] leak. In the SOCK_DGRAM branch, if connect() fails both descriptors leak, and if setup_second_end() fails sks[0] leaks. Add close() calls on all affected error paths. Assisted-by: Cursor:claude-4.6-sonnet Signed-off-by: Ankit Mahajan --- criu/sk-unix.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/criu/sk-unix.c b/criu/sk-unix.c index f309af925..186c3f5e0 100644 --- a/criu/sk-unix.c +++ b/criu/sk-unix.c @@ -1748,7 +1748,7 @@ static int open_unixsk_pair_master(struct unix_sk_info *ui, int *new_fd) tmp = dup(sk[0]); if (tmp < 0) { pr_perror("Can't dup()"); - return -1; + goto err_pair; } close(sk[0]); sk[0] = tmp; @@ -1756,18 +1756,23 @@ static int open_unixsk_pair_master(struct unix_sk_info *ui, int *new_fd) if (setup_and_serve_out(fle_peer, sk[1])) { pr_err("Can't send pair slave\n"); - return -1; + goto err_pair; } sk[1] = fle_peer->fe->fd; if (bind_unix_sk(sk[0], ui)) - return -1; + goto err; if (bind_unix_sk(sk[1], peer)) - return -1; + goto err; *new_fd = sk[0]; return 1; +err_pair: + close(sk[1]); +err: + close(sk[0]); + return -1; } static int open_unixsk_pair_slave(struct unix_sk_info *ui, int *new_fd) @@ -1862,8 +1867,11 @@ static int open_unixsk_standalone(struct unix_sk_info *ui, int *new_fd) return -1; } - if (send_criu_dump_resp(sks[1], true, true) == -1) + if (send_criu_dump_resp(sks[1], true, true) == -1) { + close(sks[0]); + close(sks[1]); return -1; + } close(sks[1]); sk = sks[0]; @@ -1882,8 +1890,11 @@ static int open_unixsk_standalone(struct unix_sk_info *ui, int *new_fd) return -1; } - if (setup_second_end(sks, file_master(&queuer->d))) + if (setup_second_end(sks, file_master(&queuer->d))) { + close(sks[0]); + close(sks[1]); return -1; + } sk = sks[0]; } else if (ui->ue->type == SOCK_DGRAM && queuer && queuer->ue->ino == FAKE_INO) { @@ -1910,11 +1921,16 @@ static int open_unixsk_standalone(struct unix_sk_info *ui, int *new_fd) */ if (connect(sk, (struct sockaddr *)&addr, sizeof(addr.sun_family))) { pr_perror("Can't clear socket's peer"); + close(sks[0]); + close(sks[1]); return -1; } - if (setup_second_end(sks, file_master(&queuer->d))) + if (setup_second_end(sks, file_master(&queuer->d))) { + close(sks[0]); + close(sks[1]); return -1; + } sk = sks[0]; } else { From 7c9212ebf3e42072608779270ff1ddfe1f5ee16e Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Thu, 2 Jul 2026 08:57:00 +0530 Subject: [PATCH 3/3] files/sk-unix: close served fds on restore errors setup_and_serve_out() can move a descriptor to the file-engine fd before a later fcntl() or serve_out_fd() failure. Returning at that point leaves callers without the fd number to close. Make setup_and_serve_out() own the descriptor it installs and close it on all failure paths. When Unix socket restore serves a second socketpair endpoint, track that installed descriptor so later bind, listen, or common socket-restore failures close both endpoints. Co-authored-by: Ankit Mahajan Signed-off-by: Radostin Stoyanov --- criu/files.c | 16 ++++++++++++-- criu/sk-unix.c | 57 +++++++++++++++++++++++++++++++------------------- 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/criu/files.c b/criu/files.c index af4b8aeac..5d72a0440 100644 --- a/criu/files.c +++ b/criu/files.c @@ -1111,25 +1111,37 @@ out: return ret; } +/* + * This helper owns new_fd. On failure it closes either new_fd or, after + * reopen_fd_as() succeeds, the descriptor moved to fle->fe->fd. + */ int setup_and_serve_out(struct fdinfo_list_entry *fle, int new_fd) { struct file_desc *d = fle->desc; pid_t pid = fle->pid; if (reopen_fd_as(fle->fe->fd, new_fd)) - return -1; + goto err; + new_fd = fle->fe->fd; if (fcntl(fle->fe->fd, F_SETFD, fle->fe->flags) == -1) { pr_perror("Unable to set file descriptor flags"); - return -1; + goto err; } BUG_ON(fle->stage != FLE_INITIALIZED); fle->stage = FLE_OPEN; + /* + * After FLE_OPEN the descriptor may already be referenced + * by other parts of restore; don't close it on failure. + */ if (serve_out_fd(pid, fle->fe->fd, d)) return -1; return 0; +err: + close(new_fd); + return -1; } static int open_fd(struct fdinfo_list_entry *fle) diff --git a/criu/sk-unix.c b/criu/sk-unix.c index 186c3f5e0..29da0990f 100644 --- a/criu/sk-unix.c +++ b/criu/sk-unix.c @@ -1721,7 +1721,7 @@ static int open_unixsk_pair_master(struct unix_sk_info *ui, int *new_fd) { struct fdinfo_list_entry *fle, *fle_peer; struct unix_sk_info *peer = ui->peer; - int sk[2], tmp; + int sk[2], ret; fle = file_master(&ui->d); pr_info_opening("master", ui, fle); @@ -1745,33 +1745,38 @@ static int open_unixsk_pair_master(struct unix_sk_info *ui, int *new_fd) * Below setup_and_serve_out() will reuse this fd, * so this dups it in something else. */ - tmp = dup(sk[0]); - if (tmp < 0) { + ret = dup(sk[0]); + if (ret < 0) { pr_perror("Can't dup()"); - goto err_pair; + goto err; } close(sk[0]); - sk[0] = tmp; + sk[0] = ret; } - if (setup_and_serve_out(fle_peer, sk[1])) { + ret = setup_and_serve_out(fle_peer, sk[1]); + sk[1] = -1; + if (ret) { pr_err("Can't send pair slave\n"); - goto err_pair; + goto err; } - sk[1] = fle_peer->fe->fd; if (bind_unix_sk(sk[0], ui)) goto err; - if (bind_unix_sk(sk[1], peer)) + if (bind_unix_sk(fle_peer->fe->fd, peer)) goto err; *new_fd = sk[0]; return 1; -err_pair: - close(sk[1]); err: - close(sk[0]); + close_safe(&sk[1]); + close_safe(&sk[0]); + /* + * Don't close fle_peer->fe->fd here: once setup_and_serve_out() + * succeeds the descriptor is at FLE_OPEN and may already be + * referenced by other restore participants. + */ return -1; } @@ -1805,6 +1810,7 @@ static int setup_second_end(int *sks, struct fdinfo_list_entry *second_end) ret = dup(sks[0]); if (ret < 0) { pr_perror("Can't dup()"); + close(sks[1]); return -1; } close(sks[0]); @@ -1822,7 +1828,7 @@ static int open_unixsk_standalone(struct unix_sk_info *ui, int *new_fd) { struct unix_sk_info *queuer = ui->queuer; struct unix_sk_info *peer = ui->peer; - struct fdinfo_list_entry *fle; + struct fdinfo_list_entry *fle, *second_end = NULL; int sk; fle = file_master(&ui->d); @@ -1877,6 +1883,7 @@ static int open_unixsk_standalone(struct unix_sk_info *ui, int *new_fd) sk = sks[0]; } else if ((ui->ue->state == TCP_ESTABLISHED && ui->ue->type != SOCK_DGRAM) && queuer && queuer->ue->ino == FAKE_INO) { + struct fdinfo_list_entry *queuer_fle; int ret, sks[2]; if (ui->ue->shutdown != SK_SHUTDOWN__BOTH) { @@ -1890,14 +1897,16 @@ static int open_unixsk_standalone(struct unix_sk_info *ui, int *new_fd) return -1; } - if (setup_second_end(sks, file_master(&queuer->d))) { + queuer_fle = file_master(&queuer->d); + if (setup_second_end(sks, queuer_fle)) { close(sks[0]); - close(sks[1]); return -1; } + second_end = queuer_fle; sk = sks[0]; } else if (ui->ue->type == SOCK_DGRAM && queuer && queuer->ue->ino == FAKE_INO) { + struct fdinfo_list_entry *queuer_fle; struct sockaddr_un addr; int sks[2]; @@ -1926,12 +1935,13 @@ static int open_unixsk_standalone(struct unix_sk_info *ui, int *new_fd) return -1; } - if (setup_second_end(sks, file_master(&queuer->d))) { + queuer_fle = file_master(&queuer->d); + if (setup_second_end(sks, queuer_fle)) { close(sks[0]); - close(sks[1]); return -1; } + second_end = queuer_fle; sk = sks[0]; } else { if (ui->ue->uflags & USK_CALLBACK) { @@ -1958,16 +1968,14 @@ static int open_unixsk_standalone(struct unix_sk_info *ui, int *new_fd) } if (bind_unix_sk(sk, ui)) { - close(sk); - return -1; + goto err; } if (ui->ue->state == TCP_LISTEN) { pr_info("\tPutting %u into listen state\n", ui->ue->ino); if (listen(sk, ui->ue->backlog) < 0) { pr_perror("Can't make usk listen"); - close(sk); - return -1; + goto err; } ui->listen = 1; wake_connected_sockets(ui); @@ -1987,10 +1995,15 @@ static int open_unixsk_standalone(struct unix_sk_info *ui, int *new_fd) out: if (restore_sk_common(sk, ui)) - return -1; + goto err; *new_fd = sk; return 0; +err: + if (second_end) + close(second_end->fe->fd); + close(sk); + return -1; } static int open_unix_sk(struct file_desc *d, int *new_fd)