Module Name: src Committed By: maxv Date: Thu Dec 27 07:56:43 UTC 2018
Modified Files: src/sys/kern: uipc_domain.c Log Message: Fix apparent race. We're doing a LIST_FOREACH, but unlock filelist_lock in the middle of the loop and drop the reference to fp. We then read fp->...le_next, but it may have been freed by another thread. This is difficult to trigger and observe, probably only KASAN can see problems of this kind. Switch to LIST_FOREACH_SAFE, and re-fetch np after re-locking. May fix PR/53674. To generate a diff of this commit: cvs rdiff -u -r1.105 -r1.106 src/sys/kern/uipc_domain.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/kern/uipc_domain.c diff -u src/sys/kern/uipc_domain.c:1.105 src/sys/kern/uipc_domain.c:1.106 --- src/sys/kern/uipc_domain.c:1.105 Sat Nov 24 17:16:44 2018 +++ src/sys/kern/uipc_domain.c Thu Dec 27 07:56:43 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: uipc_domain.c,v 1.105 2018/11/24 17:16:44 maxv Exp $ */ +/* $NetBSD: uipc_domain.c,v 1.106 2018/12/27 07:56:43 maxv Exp $ */ /* * Copyright (c) 1982, 1986, 1993 @@ -32,7 +32,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: uipc_domain.c,v 1.105 2018/11/24 17:16:44 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: uipc_domain.c,v 1.106 2018/12/27 07:56:43 maxv Exp $"); #include <sys/param.h> #include <sys/socket.h> @@ -534,7 +534,7 @@ sysctl_dounpcb(struct kinfo_pcb *pcb, co static int sysctl_unpcblist(SYSCTLFN_ARGS) { - struct file *fp, *dfp; + struct file *fp, *np, *dfp; struct socket *so; struct kinfo_pcb pcb; char *dp; @@ -583,7 +583,7 @@ sysctl_unpcblist(SYSCTLFN_ARGS) * to walk the file list looking for them. :-/ */ mutex_enter(&filelist_lock); - LIST_FOREACH(fp, &filehead, f_list) { + LIST_FOREACH_SAFE(fp, &filehead, f_list, np) { if (fp->f_count == 0 || fp->f_type != DTYPE_SOCKET || fp->f_socket == NULL) continue; @@ -615,6 +615,7 @@ sysctl_unpcblist(SYSCTLFN_ARGS) error = copyout(&pcb, dp, out_size); closef(fp); mutex_enter(&filelist_lock); + np = LIST_NEXT(dfp, f_list); LIST_REMOVE(dfp, f_list); if (error) break;