Module Name: src
Committed By: ozaki-r
Date: Thu Nov 23 07:09:20 UTC 2017
Modified Files:
src/sys/netinet6: in6.c
Log Message:
Fix a race condition of in6_ifinit
in6_ifinit checks the number of IPv6 addresses on a given interface and
if it's zero (i.e., an IPv6 address being assigned to the interface
is the first one), call if_addr_init. However, the actual assignment of
the address (ifa_insert) is out of in6_ifinit. The check and the
assignment must be done atomically.
Fix it by holding in6_ifaddr_lock during in6_ifinit and ifa_insert.
And also add missing pserialize to IFADDR_READER_FOREACH.
To generate a diff of this commit:
cvs rdiff -u -r1.253 -r1.254 src/sys/netinet6/in6.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/netinet6/in6.c
diff -u src/sys/netinet6/in6.c:1.253 src/sys/netinet6/in6.c:1.254
--- src/sys/netinet6/in6.c:1.253 Thu Nov 23 07:06:14 2017
+++ src/sys/netinet6/in6.c Thu Nov 23 07:09:20 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: in6.c,v 1.253 2017/11/23 07:06:14 ozaki-r Exp $ */
+/* $NetBSD: in6.c,v 1.254 2017/11/23 07:09:20 ozaki-r Exp $ */
/* $KAME: in6.c,v 1.198 2001/07/18 09:12:38 itojun Exp $ */
/*
@@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.253 2017/11/23 07:06:14 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.254 2017/11/23 07:09:20 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@@ -1258,30 +1258,36 @@ in6_update_ifa1(struct ifnet *ifp, struc
*/
ifaref(&ia->ia_ifa);
}
+
+ /* Must execute in6_ifinit and ifa_insert atomically */
+ mutex_enter(&in6_ifaddr_lock);
+
/* reset the interface and routing table appropriately. */
error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew);
if (error != 0) {
if (hostIsNew)
free(ia, M_IFADDR);
+ mutex_exit(&in6_ifaddr_lock);
return error;
}
/*
* We are done if we have simply modified an existing address.
*/
- if (!hostIsNew)
+ if (!hostIsNew) {
+ mutex_exit(&in6_ifaddr_lock);
return error;
+ }
/*
* Insert ia to the global list and ifa to the interface's list.
* A reference to it is already gained above.
*/
- mutex_enter(&in6_ifaddr_lock);
IN6_ADDRLIST_WRITER_INSERT_TAIL(ia);
- mutex_exit(&in6_ifaddr_lock);
-
ifa_insert(ifp, &ia->ia_ifa);
+ mutex_exit(&in6_ifaddr_lock);
+
/*
* Beyond this point, we should call in6_purgeaddr upon an error,
* not just go to unlink.
@@ -1755,28 +1761,30 @@ in6_ifinit(struct ifnet *ifp, struct in6
const struct sockaddr_in6 *sin6, int newhost)
{
int error = 0, ifacount = 0;
- int s = splsoftnet();
+ int s;
struct ifaddr *ifa;
+ KASSERT(mutex_owned(&in6_ifaddr_lock));
+
/*
* Give the interface a chance to initialize
* if this is its first address,
* and to validate the address if necessary.
*/
+ s = pserialize_read_enter();
IFADDR_READER_FOREACH(ifa, ifp) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
ifacount++;
}
+ pserialize_read_exit(s);
ia->ia_addr = *sin6;
if (ifacount == 0 &&
(error = if_addr_init(ifp, &ia->ia_ifa, true)) != 0) {
- splx(s);
return error;
}
- splx(s);
ia->ia_ifa.ifa_metric = ifp->if_metric;