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;