Source-Changes-HG archive

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

[src/trunk]: src/sys PR kern/50522: gif(4) ioctl causes panic while someone i...



details:   https://anonhg.NetBSD.org/src/rev/6dcf5c61e3ef
branches:  trunk
changeset: 812324:6dcf5c61e3ef
user:      knakahara <knakahara%NetBSD.org@localhost>
date:      Fri Dec 11 07:59:14 2015 +0000

description:
PR kern/50522: gif(4) ioctl causes panic while someone is using the gif(4) interface.

It is required to wait other CPU's softint completion before disestablishing
the softint handler.

diffstat:

 sys/net/if_gif.c       |  99 ++++++++++++++++++++++++++++++++++++++++++++++---
 sys/net/if_gif.h       |  18 ++++++++-
 sys/netinet/in_gif.c   |  15 +++++--
 sys/netinet6/in6_gif.c |  14 +++++-
 4 files changed, 132 insertions(+), 14 deletions(-)

diffs (truncated from 317 to 300 lines):

diff -r d67db38611c6 -r 6dcf5c61e3ef sys/net/if_gif.c
--- a/sys/net/if_gif.c  Fri Dec 11 07:21:09 2015 +0000
+++ b/sys/net/if_gif.c  Fri Dec 11 07:59:14 2015 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: if_gif.c,v 1.101 2015/12/11 04:29:24 knakahara Exp $   */
+/*     $NetBSD: if_gif.c,v 1.102 2015/12/11 07:59:14 knakahara Exp $   */
 /*     $KAME: if_gif.c,v 1.76 2001/08/20 02:01:02 kjc Exp $    */
 
 /*
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_gif.c,v 1.101 2015/12/11 04:29:24 knakahara Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_gif.c,v 1.102 2015/12/11 07:59:14 knakahara Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -53,6 +53,7 @@
 #include <sys/cpu.h>
 #include <sys/intr.h>
 #include <sys/kmem.h>
+#include <sys/atomic.h>
 
 #include <net/if.h>
 #include <net/if_types.h>
@@ -146,6 +147,10 @@
 gifattach0(struct gif_softc *sc)
 {
 
+       sc->gif_si_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NONE);
+       KASSERT(sc->gif_si_lock != NULL);
+       cv_init(&sc->gif_si_cv, "if_gif_cv");
+       sc->gif_si_refs = 0;
        sc->encap_cookie4 = sc->encap_cookie6 = NULL;
 
        sc->gif_if.if_addrlen = 0;
@@ -174,6 +179,8 @@
        if_detach(ifp);
        rtcache_free(&sc->gif_ro);
 
+       cv_destroy(&sc->gif_si_cv);
+       mutex_obj_free(sc->gif_si_lock);
        kmem_free(sc, sizeof(struct gif_softc));
 
        return (0);
@@ -295,7 +302,8 @@
 
        m->m_flags &= ~(M_BCAST|M_MCAST);
        if (!(ifp->if_flags & IFF_UP) ||
-           sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
+           sc->gif_psrc == NULL || sc->gif_pdst == NULL ||
+           sc->gif_si == NULL) {
                m_freem(m);
                error = ENETDOWN;
                goto end;
@@ -321,9 +329,11 @@
                splx(s);
                goto end;
        }
+
+       /* softint_schedule() must be called with kpreempt_disabled() */
+       softint_schedule(sc->gif_si);
        splx(s);
 
-       softint_schedule(sc->gif_si);
        error = 0;
 
   end:
@@ -346,6 +356,24 @@
        sc = arg;
        ifp = &sc->gif_if;
 
+       atomic_inc_uint(&sc->gif_si_refs);
+
+       /*
+        * pattern (a) (see also gif_set_tunnel())
+        * other CPUs does {set,delete}_tunnel after curcpu have done
+        * softint_schedule().
+        */
+       if (sc->gif_pdst == NULL || sc->gif_psrc == NULL) {
+               IFQ_PURGE(&ifp->if_snd);
+
+               if (atomic_dec_uint_nv(&sc->gif_si_refs) == 0) {
+                       mutex_enter(sc->gif_si_lock);
+                       cv_broadcast(&sc->gif_si_cv);
+                       mutex_exit(sc->gif_si_lock);
+               }
+               return;
+       }
+
        /* output processing */
        while (1) {
                s = splnet();
@@ -397,6 +425,16 @@
                        ifp->if_obytes += len;
                }
        }
+
+       /*
+        * pattern (b) (see also gif_set_tunnel())
+        * other CPUs begin {set,delete}_tunnel while curcpu si doing gifintr.
+        */
+       if (atomic_dec_uint_nv(&sc->gif_si_refs) == 0) {
+               mutex_enter(sc->gif_si_lock);
+               cv_broadcast(&sc->gif_si_cv);
+               mutex_exit(sc->gif_si_lock);
+       }
 }
 
 void
@@ -744,6 +782,7 @@
        struct gif_softc *sc2;
        struct sockaddr *osrc, *odst;
        struct sockaddr *nsrc, *ndst;
+       void *osi;
        int s;
        int error;
 
@@ -777,8 +816,38 @@
 
        /* Firstly, clear old configurations. */
        if (sc->gif_si) {
-               softint_disestablish(sc->gif_si);
+               osrc = sc->gif_psrc;
+               odst = sc->gif_pdst;
+               osi = sc->gif_si;
+               sc->gif_psrc = NULL;
+               sc->gif_pdst = NULL;
                sc->gif_si = NULL;
+
+               /*
+                * At this point, gif_output() does not softint_schedule()
+                * any more. However, there are below 2 fears of other CPUs.
+                *     (a) gif_output() has done softint_schedule(),and softint
+                *         (gifintr()) is waiting for execution
+                *     (b) gifintr() is already running
+                * see also gifintr()
+                */
+
+               /*
+                * To avoid the above fears, wait for gifintr() completion of
+                * all CPUs here.
+                */
+               mutex_enter(sc->gif_si_lock);
+               while (sc->gif_si_refs > 0) {
+                       aprint_debug("%s: cv_wait on gif_softc\n", __func__);
+                       cv_wait(&sc->gif_si_cv, sc->gif_si_lock);
+               }
+               mutex_exit(sc->gif_si_lock);
+
+               softint_disestablish(osi);
+               sc->gif_psrc = osrc;
+               sc->gif_pdst = odst;
+               osrc = NULL;
+               odst = NULL;
        }
        /* XXX we can detach from both, but be polite just in case */
        if (sc->gif_psrc)
@@ -845,13 +914,31 @@
 gif_delete_tunnel(struct ifnet *ifp)
 {
        struct gif_softc *sc = ifp->if_softc;
+       struct sockaddr *osrc, *odst;
+       void *osi;
        int s;
 
        s = splsoftnet();
 
        if (sc->gif_si) {
-               softint_disestablish(sc->gif_si);
+               osrc = sc->gif_psrc;
+               odst = sc->gif_pdst;
+               osi = sc->gif_si;
+
+               sc->gif_psrc = NULL;
+               sc->gif_pdst = NULL;
                sc->gif_si = NULL;
+
+               mutex_enter(sc->gif_si_lock);
+               while (sc->gif_si_refs > 0) {
+                       aprint_debug("%s: cv_wait on gif_softc\n", __func__);
+                       cv_wait(&sc->gif_si_cv, sc->gif_si_lock);
+               }
+               mutex_exit(sc->gif_si_lock);
+
+               softint_disestablish(osi);
+               sc->gif_psrc = osrc;
+               sc->gif_pdst = odst;
        }
        if (sc->gif_psrc) {
                sockaddr_free(sc->gif_psrc);
diff -r d67db38611c6 -r 6dcf5c61e3ef sys/net/if_gif.h
--- a/sys/net/if_gif.h  Fri Dec 11 07:21:09 2015 +0000
+++ b/sys/net/if_gif.h  Fri Dec 11 07:59:14 2015 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: if_gif.h,v 1.19 2008/11/12 12:36:28 ad Exp $   */
+/*     $NetBSD: if_gif.h,v 1.20 2015/12/11 07:59:14 knakahara Exp $    */
 /*     $KAME: if_gif.h,v 1.23 2001/07/27 09:21:42 itojun Exp $ */
 
 /*
@@ -38,6 +38,8 @@
 #define _NET_IF_GIF_H_
 
 #include <sys/queue.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -60,11 +62,21 @@
        const struct encaptab *encap_cookie6;
        LIST_ENTRY(gif_softc) gif_list; /* list of all gifs */
        void    *gif_si;                /* softintr handle */
+
+       struct si_sync { /* can access without gif_lock */
+               unsigned int    si_refs;        /* reference count for gif_si */
+               kcondvar_t      si_cv;          /* wait for softint completion */
+               kmutex_t        *si_lock;       /* lock for gif_si_sync */
+       } gif_si_sync;
 };
 #define GIF_ROUTE_TTL  10
 
 #define gif_ro gifsc_gifscr.gifscr_ro
 
+#define gif_si_refs    gif_si_sync.si_refs
+#define gif_si_cv      gif_si_sync.si_cv
+#define gif_si_lock    gif_si_sync.si_lock
+
 #define GIF_MTU                (1280)  /* Default MTU */
 #define        GIF_MTU_MIN     (1280)  /* Minimum MTU */
 #define        GIF_MTU_MAX     (8192)  /* Maximum MTU */
@@ -81,4 +93,8 @@
 int    gif_encapcheck(struct mbuf *, int, int, void *);
 #endif
 
+/*
+ * Locking notes:
+ * - All members of struct si_sync are protected by si_lock (an adaptive mutex)
+ */
 #endif /* !_NET_IF_GIF_H_ */
diff -r d67db38611c6 -r 6dcf5c61e3ef sys/netinet/in_gif.c
--- a/sys/netinet/in_gif.c      Fri Dec 11 07:21:09 2015 +0000
+++ b/sys/netinet/in_gif.c      Fri Dec 11 07:59:14 2015 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: in_gif.c,v 1.65 2015/08/24 22:21:26 pooka Exp $        */
+/*     $NetBSD: in_gif.c,v 1.66 2015/12/11 07:59:14 knakahara Exp $    */
 /*     $KAME: in_gif.c,v 1.66 2001/07/29 04:46:09 itojun Exp $ */
 
 /*
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in_gif.c,v 1.65 2015/08/24 22:21:26 pooka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in_gif.c,v 1.66 2015/12/11 07:59:14 knakahara Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -223,13 +223,20 @@
                return;
        }
 #ifndef GIF_ENCAPCHECK
-       if (!gif_validate4(ip, gifp->if_softc, m->m_pkthdr.rcvif)) {
+       struct gif_softc *sc = (struct gif_softc *)gifp->if_softc;
+       /* other CPU do delete_tunnel */
+       if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
+               m_freem(m);
+               ip_statinc(IP_STAT_NOGIF);
+               return;
+       }
+
+       if (!gif_validate4(ip, sc, m->m_pkthdr.rcvif)) {
                m_freem(m);
                ip_statinc(IP_STAT_NOGIF);
                return;
        }
 #endif
-
        otos = ip->ip_tos;
        m_adj(m, off);
 
diff -r d67db38611c6 -r 6dcf5c61e3ef sys/netinet6/in6_gif.c
--- a/sys/netinet6/in6_gif.c    Fri Dec 11 07:21:09 2015 +0000
+++ b/sys/netinet6/in6_gif.c    Fri Dec 11 07:59:14 2015 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: in6_gif.c,v 1.62 2015/08/24 22:21:27 pooka Exp $       */
+/*     $NetBSD: in6_gif.c,v 1.63 2015/12/11 07:59:14 knakahara Exp $   */
 /*     $KAME: in6_gif.c,v 1.62 2001/07/29 04:27:25 itojun Exp $        */
 
 /*
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6_gif.c,v 1.62 2015/08/24 22:21:27 pooka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6_gif.c,v 1.63 2015/12/11 07:59:14 knakahara Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"



Home | Main Index | Thread Index | Old Index