Module Name: src Committed By: knakahara Date: Fri Dec 11 07:59:14 UTC 2015
Modified Files: src/sys/net: if_gif.c if_gif.h src/sys/netinet: in_gif.c src/sys/netinet6: in6_gif.c Log Message: 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. To generate a diff of this commit: cvs rdiff -u -r1.101 -r1.102 src/sys/net/if_gif.c cvs rdiff -u -r1.19 -r1.20 src/sys/net/if_gif.h cvs rdiff -u -r1.65 -r1.66 src/sys/netinet/in_gif.c cvs rdiff -u -r1.62 -r1.63 src/sys/netinet6/in6_gif.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/net/if_gif.c diff -u src/sys/net/if_gif.c:1.101 src/sys/net/if_gif.c:1.102 --- src/sys/net/if_gif.c:1.101 Fri Dec 11 04:29:24 2015 +++ src/sys/net/if_gif.c Fri Dec 11 07:59:14 2015 @@ -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 @@ __KERNEL_RCSID(0, "$NetBSD: if_gif.c,v 1 #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 @@ void 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 @@ gif_clone_destroy(struct ifnet *ifp) 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 @@ gif_output(struct ifnet *ifp, struct mbu 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 @@ gif_output(struct ifnet *ifp, struct mbu splx(s); goto end; } - splx(s); + /* softint_schedule() must be called with kpreempt_disabled() */ softint_schedule(sc->gif_si); + splx(s); + error = 0; end: @@ -346,6 +356,24 @@ gifintr(void *arg) 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 @@ gifintr(void *arg) 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 @@ gif_set_tunnel(struct ifnet *ifp, struct struct gif_softc *sc2; struct sockaddr *osrc, *odst; struct sockaddr *nsrc, *ndst; + void *osi; int s; int error; @@ -777,8 +816,38 @@ gif_set_tunnel(struct ifnet *ifp, struct /* 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 @@ void 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); Index: src/sys/net/if_gif.h diff -u src/sys/net/if_gif.h:1.19 src/sys/net/if_gif.h:1.20 --- src/sys/net/if_gif.h:1.19 Wed Nov 12 12:36:28 2008 +++ src/sys/net/if_gif.h Fri Dec 11 07:59:14 2015 @@ -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 @@ struct gif_softc { 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 @@ void gif_delete_tunnel(struct ifnet *); 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_ */ Index: src/sys/netinet/in_gif.c diff -u src/sys/netinet/in_gif.c:1.65 src/sys/netinet/in_gif.c:1.66 --- src/sys/netinet/in_gif.c:1.65 Mon Aug 24 22:21:26 2015 +++ src/sys/netinet/in_gif.c Fri Dec 11 07:59:14 2015 @@ -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 @@ in_gif_input(struct mbuf *m, ...) 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; } -#endif + 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); Index: src/sys/netinet6/in6_gif.c diff -u src/sys/netinet6/in6_gif.c:1.62 src/sys/netinet6/in6_gif.c:1.63 --- src/sys/netinet6/in6_gif.c:1.62 Mon Aug 24 22:21:27 2015 +++ src/sys/netinet6/in6_gif.c Fri Dec 11 07:59:14 2015 @@ -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" @@ -224,7 +224,15 @@ in6_gif_input(struct mbuf **mp, int *off return IPPROTO_DONE; } #ifndef GIF_ENCAPCHECK - if (!gif_validate6(ip6, 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); + IP6_STATINC(IP6_STAT_NOGIF); + return IPPROTO_DONE; + } + + if (!gif_validate6(ip6, sc, m->m_pkthdr.rcvif)) { m_freem(m); IP6_STATINC(IP6_STAT_NOGIF); return IPPROTO_DONE;