diff options
| author | ozaki-r <ozaki-r@NetBSD.org> | 2016-05-12 02:24:16 +0000 |
|---|---|---|
| committer | ozaki-r <ozaki-r@NetBSD.org> | 2016-05-12 02:24:16 +0000 |
| commit | bb387e19984023842addb15f673be6e758826f0a (patch) | |
| tree | 225d25c03185cfe2da8efff3368577a4ca8809e7 /sys/compat/linux/common/linux_socket.c | |
| parent | 0d14b3a658d08fc046d51fde52f0f5cb757382fa (diff) | |
Protect ifnet list with psz and psref
The change ensures that ifnet objects in the ifnet list aren't freed during
list iterations by using pserialize(9) and psref(9).
Note that the change adds a pslist(9) for ifnet but doesn't remove the
original ifnet list (ifnet_list) to avoid breaking kvm(3) users. We
shouldn't use the original list in the kernel anymore.
Diffstat (limited to 'sys/compat/linux/common/linux_socket.c')
| -rw-r--r-- | sys/compat/linux/common/linux_socket.c | 53 |
1 files changed, 40 insertions, 13 deletions
diff --git a/sys/compat/linux/common/linux_socket.c b/sys/compat/linux/common/linux_socket.c index 7b7e07d3583..2c2e7cdb6d4 100644 --- a/sys/compat/linux/common/linux_socket.c +++ b/sys/compat/linux/common/linux_socket.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_socket.c,v 1.126 2015/07/24 13:02:52 maxv Exp $ */ +/* $NetBSD: linux_socket.c,v 1.127 2016/05/12 02:24:16 ozaki-r Exp $ */ /*- * Copyright (c) 1995, 1998, 2008 The NetBSD Foundation, Inc. @@ -35,7 +35,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: linux_socket.c,v 1.126 2015/07/24 13:02:52 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: linux_socket.c,v 1.127 2016/05/12 02:24:16 ozaki-r Exp $"); #if defined(_KERNEL_OPT) #include "opt_inet.h" @@ -1118,6 +1118,9 @@ linux_getifconf(struct lwp *l, register_t *retval, void *data) int space = 0, error; const int sz = (int)sizeof(ifr); bool docopy; + int s; + int bound = curlwp->l_pflag & LP_BOUND; + struct psref psref; error = copyin(data, &ifc, sizeof(ifc)); if (error) @@ -1129,11 +1132,18 @@ linux_getifconf(struct lwp *l, register_t *retval, void *data) ifrp = ifc.ifc_req; } - IFNET_FOREACH(ifp) { + curlwp->l_pflag |= LP_BOUND; + s = pserialize_read_enter(); + IFNET_READER_FOREACH(ifp) { + psref_acquire(&psref, &ifp->if_psref, ifnet_psref_class); + pserialize_read_exit(s); + (void)strncpy(ifr.ifr_name, ifp->if_xname, sizeof(ifr.ifr_name)); - if (ifr.ifr_name[sizeof(ifr.ifr_name) - 1] != '\0') - return ENAMETOOLONG; + if (ifr.ifr_name[sizeof(ifr.ifr_name) - 1] != '\0') { + error = ENAMETOOLONG; + goto release_exit; + } if (IFADDR_EMPTY(ifp)) continue; IFADDR_FOREACH(ifa, ifp) { @@ -1147,12 +1157,17 @@ linux_getifconf(struct lwp *l, register_t *retval, void *data) if (space >= sz) { error = copyout(&ifr, ifrp, sz); if (error != 0) - return error; + goto release_exit; ifrp++; } space -= sz; } + + s = pserialize_read_enter(); + psref_release(&psref, &ifp->if_psref, ifnet_psref_class); } + pserialize_read_exit(s); + curlwp->l_pflag ^= bound ^ LP_BOUND; if (docopy) ifc.ifc_len -= space; @@ -1160,6 +1175,11 @@ linux_getifconf(struct lwp *l, register_t *retval, void *data) ifc.ifc_len = -space; return copyout(&ifc, data, sizeof(ifc)); + +release_exit: + psref_release(&psref, &ifp->if_psref, ifnet_psref_class); + curlwp->l_pflag ^= bound ^ LP_BOUND; + return error; } int @@ -1174,6 +1194,7 @@ linux_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, struct sockaddr_dl *sadl; int error, found; int index, ifnum; + int s; /* * We can't emulate this ioctl by calling sys_ioctl() to run @@ -1205,14 +1226,17 @@ linux_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, * Try real interface name first, then fake "ethX" */ found = 0; - IFNET_FOREACH(ifp) { + s = pserialize_read_enter(); + IFNET_READER_FOREACH(ifp) { if (found) break; if (strcmp(lreq.ifr_name, ifp->if_xname)) /* not this interface */ continue; + found=1; if (IFADDR_EMPTY(ifp)) { + pserialize_read_exit(s); error = ENODEV; goto out; } @@ -1228,10 +1252,13 @@ linux_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, sizeof(lreq.ifr_hwaddr.sa_data))); lreq.ifr_hwaddr.sa_family = sadl->sdl_family; + pserialize_read_exit(s); + error = copyout(&lreq, data, sizeof(lreq)); goto out; } } + pserialize_read_exit(s); if (strncmp(lreq.ifr_name, "eth", 3) != 0) { /* unknown interface, not even an "eth*" name */ @@ -1247,10 +1274,8 @@ linux_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, } error = EINVAL; /* in case we don't find one */ - found = 0; - IFNET_FOREACH(ifp) { - if (found) - break; + s = pserialize_read_enter(); + IFNET_READER_FOREACH(ifp) { memcpy(lreq.ifr_name, ifp->if_xname, MIN(LINUX_IFNAMSIZ, IFNAMSIZ)); IFADDR_FOREACH(ifa, ifp) { @@ -1269,11 +1294,13 @@ linux_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, sizeof(lreq.ifr_hwaddr.sa_data))); lreq.ifr_hwaddr.sa_family = sadl->sdl_family; + pserialize_read_exit(s); + error = copyout(&lreq, data, sizeof(lreq)); - found = 1; - break; + goto out; } } + pserialize_read_exit(s); out: KERNEL_UNLOCK_ONE(NULL); |
