When a task exits while still having dead cyclic reference AF_UNIX sockets, those sockets stay behind indefinitely until the garbage collector gets scheduled by an unrelated reason. This can be observed with the program below. Resolve this issue by scheduling the garbage collector during task exit, after the task's file descriptors have been closed. static int send_fd(int unix_fd, int fd) { struct msghdr msgh; struct cmsghdr *cmsg; char buf[CMSG_SPACE(sizeof(fd))]; memset(&msgh, 0, sizeof(msgh)); memset(buf, 0, sizeof(buf)); msgh.msg_control = buf; msgh.msg_controllen = sizeof(buf); cmsg = CMSG_FIRSTHDR(&msgh); cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; msgh.msg_controllen = cmsg->cmsg_len; memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); return sendmsg(unix_fd, &msgh, 0); } int main(int argc, char *argv[]) { int fd[2]; int i; for (int n = 0; n < 100; ++n) { if (socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fd) == -1) goto out_error; for (i = 0; i < 100; ++i) { if (send_fd(fd[0], fd[0]) == -1) goto out_error; if (send_fd(fd[1], fd[1]) == -1) goto out_error; } } return 0; out_error: fprintf(stderr, "error: %s\n", strerror(errno)); } Signed-off-by: Nam Cao --- include/net/af_unix.h | 5 +++++ kernel/exit.c | 2 ++ net/unix/af_unix.h | 1 - 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/net/af_unix.h b/include/net/af_unix.h index 34f53dde65ce..686f6f1d1c21 100644 --- a/include/net/af_unix.h +++ b/include/net/af_unix.h @@ -14,11 +14,16 @@ #if IS_ENABLED(CONFIG_UNIX) struct unix_sock *unix_get_socket(struct file *filp); +void unix_schedule_gc(struct user_struct *user); #else static inline struct unix_sock *unix_get_socket(struct file *filp) { return NULL; } + +static inline void unix_schedule_gc(struct user_struct *user) +{ +} #endif struct unix_address { diff --git a/kernel/exit.c b/kernel/exit.c index 2c0b1c02920f..ba9404844523 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -71,6 +71,7 @@ #include #include #include +#include #include @@ -1002,6 +1003,7 @@ void __noreturn do_exit(long code) exit_sem(tsk); exit_shm(tsk); exit_files(tsk); + unix_schedule_gc(NULL); /* Must be after exit_files() */ exit_fs(tsk); if (group_dead) disassociate_ctty(1); diff --git a/net/unix/af_unix.h b/net/unix/af_unix.h index 8119dbeef3a3..fc4c59893124 100644 --- a/net/unix/af_unix.h +++ b/net/unix/af_unix.h @@ -30,7 +30,6 @@ void unix_update_edges(struct unix_sock *receiver); int unix_prepare_fpl(struct scm_fp_list *fpl); void unix_destroy_fpl(struct scm_fp_list *fpl); void unix_peek_fpl(struct scm_fp_list *fpl); -void unix_schedule_gc(struct user_struct *user); /* SOCK_DIAG */ long unix_inq_len(struct sock *sk); -- 2.47.3