Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/netinet6 Fix a race condition of in6_ifinit



details:   https://anonhg.NetBSD.org/src/rev/07bcb6b50c2f
branches:  trunk
changeset: 357726:07bcb6b50c2f
user:      ozaki-r <ozaki-r%NetBSD.org@localhost>
date:      Thu Nov 23 07:09:20 2017 +0000

description:
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.

diffstat:

 sys/netinet6/in6.c |  26 +++++++++++++++++---------
 1 files changed, 17 insertions(+), 9 deletions(-)

diffs (93 lines):

diff -r 8c57d9057e82 -r 07bcb6b50c2f sys/netinet6/in6.c
--- a/sys/netinet6/in6.c        Thu Nov 23 07:06:14 2017 +0000
+++ b/sys/netinet6/in6.c        Thu Nov 23 07:09:20 2017 +0000
@@ -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 @@
                 */
                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);
+       ifa_insert(ifp, &ia->ia_ifa);
+
        mutex_exit(&in6_ifaddr_lock);
 
-       ifa_insert(ifp, &ia->ia_ifa);
-
        /*
         * Beyond this point, we should call in6_purgeaddr upon an error,
         * not just go to unlink.
@@ -1755,28 +1761,30 @@
        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;
 



Home | Main Index | Thread Index | Old Index