DF-3298 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -719,6 +719,16 @@ bpfdetach(ifp); if_detach(ifp); + /* + * The carp(4) ifnet is embedded in the softc that is about to be + * freed below. Purge any IP fragments still sitting in the + * reassembly queues that were received through this ifnet: the + * fragment mbufs carry m_pkthdr.rcvif pointing at this ifnet and + * a datagram completing reassembly after the free would dispatch + * into carp_proto_input() with a dangling rcvif (use-after-free). + */ + ip_reass_purge_ifnet(ifp); + KASSERT(sc->sc_naddrs == 0, ("certain inet address is still active")); kfree(sc, M_CARP); --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -309,6 +309,7 @@ static void ipfrag_timeo_dispatch(netmsg_t); static void ipfrag_timeo(void *); static void ipfrag_drain_dispatch(netmsg_t); +static void ip_reass_purge_ifnet_dispatch(netmsg_t); /* * IP initialization: fill in IP protocol switch table. @@ -1513,6 +1514,64 @@ in_rtqdrain(); } +struct netmsg_purge_ifnet { + struct netmsg_base base; + struct ifnet *ifp; +}; + +static void +ip_reass_purge_ifnet_dispatch(netmsg_t nmsg) +{ + struct netmsg_purge_ifnet *msg = (struct netmsg_purge_ifnet *)nmsg; + struct ifnet *ifp = msg->ifp; + struct ipfrag_queue *fragq = &ipfrag_queue_pcpu[mycpuid]; + struct ipq *fp, *fpnext; + struct mbuf *m; + int i; + + for (i = 0; i < IPREASS_NHASH; i++) { + TAILQ_FOREACH_MUTABLE(fp, &fragq->ipq[i], ipq_list, fpnext) { + for (m = fp->ipq_frags; m != NULL; m = m->m_nextpkt) { + if (m->m_pkthdr.rcvif == ifp) { + ipstat.ips_fragdropped += + fp->ipq_nfrags; + ip_freef(fragq, &fragq->ipq[i], fp); + break; + } + } + } + } + + crit_enter(); + lwkt_replymsg(&nmsg->lmsg, 0); + crit_exit(); +} + +/* + * Synchronously purge, on all protocol threads, every IP reassembly + * queue holding fragments that were received via the given ifnet. + * + * This must be called before an ifnet whose softc embeds it (e.g. a + * carp(4) interface) is freed: fragment mbufs reference the ifnet via + * m_pkthdr.rcvif, and a datagram whose reassembly completes after the + * free would be dispatched up the transport stack with a dangling + * rcvif, causing a use-after-free in the protocol input path + * (e.g. carp_proto_input()). + */ +void +ip_reass_purge_ifnet(struct ifnet *ifp) +{ + struct netmsg_purge_ifnet msg; + int cpu; + + for (cpu = 0; cpu < netisr_ncpus; ++cpu) { + netmsg_init(&msg.base, NULL, &curthread->td_msgport, 0, + ip_reass_purge_ifnet_dispatch); + msg.ifp = ifp; + netisr_domsg(&msg.base, cpu); + } +} + /* * Do option processing on a datagram, * possibly discarding it if bad options are encountered, --- a/sys/netinet/ip_var.h +++ b/sys/netinet/ip_var.h @@ -186,6 +186,7 @@ void ip_ctloutput(union netmsg *); void ip_drain(void); +void ip_reass_purge_ifnet(struct ifnet *); int ip_fragment(struct ip *ip, struct mbuf **m_frag, int mtu, u_long if_hwassist_flags, int sw_csum); struct mbuf * |