Fix panic in network stack due to memory use after free in relation to
fragmented packets. When sending IPv4 and IPv6 fragmented packets and a fragment is lost, the mbuf making up the fragment will remain in the temporary hashed fragment list for a while. If the network interface departs before the so-called slow timeout clears the packet, the fragment causes a panic when the timeout kicks in due to accessing a freed network interface structure. Make sure that when a network device is departing, all hashed IPv4 and IPv6 fragments belonging to it, get freed. Backtrace: panic() icmp6_reflect() hlim = ND_IFINFO(m->m_pkthdr.rcvif)->chlim; ^^^^ rcvif->if_afdata[AF_INET6] is NULL. icmp6_error() frag6_freef() frag6_slowtimo() pfslowtimo() softclock_call_cc() softclock() ithread_loop() Differential Revision: https://reviews.freebsd.org/D19622 Reviewed by: bz (network), adrian MFC after: 1 week Sponsored by: Mellanox Technologies
This commit is contained in:
parent
83efd2885e
commit
40eb389666
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=346530
@ -46,7 +46,10 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/rss_config.h>
|
||||
#include <net/netisr.h>
|
||||
#include <net/vnet.h>
|
||||
@ -606,6 +609,37 @@ ipreass_drain(void)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Drain off all datagram fragments belonging to
|
||||
* the given network interface.
|
||||
*/
|
||||
static void
|
||||
ipreass_cleanup(void *arg __unused, struct ifnet *ifp)
|
||||
{
|
||||
struct ipq *fp, *temp;
|
||||
struct mbuf *m;
|
||||
int i;
|
||||
|
||||
KASSERT(ifp != NULL, ("%s: ifp is NULL", __func__));
|
||||
|
||||
CURVNET_SET_QUIET(ifp->if_vnet);
|
||||
for (i = 0; i < IPREASS_NHASH; i++) {
|
||||
IPQ_LOCK(i);
|
||||
/* Scan fragment list. */
|
||||
TAILQ_FOREACH_SAFE(fp, &V_ipq[i].head, ipq_list, temp) {
|
||||
for (m = fp->ipq_frags; m != NULL; m = m->m_nextpkt) {
|
||||
if (m->m_pkthdr.rcvif == ifp) {
|
||||
ipq_drop(&V_ipq[i], fp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
IPQ_UNLOCK(i);
|
||||
}
|
||||
CURVNET_RESTORE();
|
||||
}
|
||||
EVENTHANDLER_DEFINE(ifnet_departure_event, ipreass_cleanup, NULL, 0);
|
||||
|
||||
#ifdef VIMAGE
|
||||
/*
|
||||
* Destroy IP reassembly structures.
|
||||
|
@ -81,7 +81,7 @@ static void frag6_deq(struct ip6asfrag *, uint32_t bucket __unused);
|
||||
static void frag6_insque_head(struct ip6q *, struct ip6q *,
|
||||
uint32_t bucket);
|
||||
static void frag6_remque(struct ip6q *, uint32_t bucket);
|
||||
static void frag6_freef(struct ip6q *, uint32_t bucket);
|
||||
static void frag6_freef(struct ip6q *, uint32_t bucket, bool send_icmp);
|
||||
|
||||
struct ip6qbucket {
|
||||
struct ip6q ip6q;
|
||||
@ -594,7 +594,7 @@ frag6_input(struct mbuf **mp, int *offp, int proto)
|
||||
if (af6->ip6af_off != next) {
|
||||
if (q6->ip6q_nfrag > V_ip6_maxfragsperpacket) {
|
||||
IP6STAT_ADD(ip6s_fragdropped, q6->ip6q_nfrag);
|
||||
frag6_freef(q6, hash);
|
||||
frag6_freef(q6, hash, true);
|
||||
}
|
||||
IP6Q_UNLOCK(hash);
|
||||
return IPPROTO_DONE;
|
||||
@ -604,7 +604,7 @@ frag6_input(struct mbuf **mp, int *offp, int proto)
|
||||
if (af6->ip6af_up->ip6af_mff) {
|
||||
if (q6->ip6q_nfrag > V_ip6_maxfragsperpacket) {
|
||||
IP6STAT_ADD(ip6s_fragdropped, q6->ip6q_nfrag);
|
||||
frag6_freef(q6, hash);
|
||||
frag6_freef(q6, hash, true);
|
||||
}
|
||||
IP6Q_UNLOCK(hash);
|
||||
return IPPROTO_DONE;
|
||||
@ -731,7 +731,7 @@ frag6_input(struct mbuf **mp, int *offp, int proto)
|
||||
* associated datagrams.
|
||||
*/
|
||||
static void
|
||||
frag6_freef(struct ip6q *q6, uint32_t bucket)
|
||||
frag6_freef(struct ip6q *q6, uint32_t bucket, bool send_icmp)
|
||||
{
|
||||
struct ip6asfrag *af6, *down6;
|
||||
|
||||
@ -748,7 +748,7 @@ frag6_freef(struct ip6q *q6, uint32_t bucket)
|
||||
* Return ICMP time exceeded error for the 1st fragment.
|
||||
* Just free other fragments.
|
||||
*/
|
||||
if (af6->ip6af_off == 0) {
|
||||
if (af6->ip6af_off == 0 && send_icmp != false) {
|
||||
struct ip6_hdr *ip6;
|
||||
|
||||
/* adjust pointer */
|
||||
@ -864,7 +864,7 @@ frag6_slowtimo(void)
|
||||
IP6STAT_ADD(ip6s_fragtimeout,
|
||||
q6->ip6q_prev->ip6q_nfrag);
|
||||
/* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
|
||||
frag6_freef(q6->ip6q_prev, i);
|
||||
frag6_freef(q6->ip6q_prev, i, true);
|
||||
}
|
||||
}
|
||||
/*
|
||||
@ -883,7 +883,7 @@ frag6_slowtimo(void)
|
||||
IP6STAT_ADD(ip6s_fragoverflow,
|
||||
q6->ip6q_prev->ip6q_nfrag);
|
||||
/* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
|
||||
frag6_freef(head->ip6q_prev, i);
|
||||
frag6_freef(head->ip6q_prev, i, true);
|
||||
}
|
||||
IP6Q_UNLOCK(i);
|
||||
}
|
||||
@ -901,7 +901,7 @@ frag6_slowtimo(void)
|
||||
IP6STAT_ADD(ip6s_fragoverflow,
|
||||
q6->ip6q_prev->ip6q_nfrag);
|
||||
/* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
|
||||
frag6_freef(head->ip6q_prev, i);
|
||||
frag6_freef(head->ip6q_prev, i, true);
|
||||
}
|
||||
IP6Q_UNLOCK(i);
|
||||
i = (i + 1) % IP6REASS_NHASH;
|
||||
@ -931,7 +931,7 @@ frag6_drain(void)
|
||||
while (head->ip6q_next != head) {
|
||||
IP6STAT_INC(ip6s_fragdropped);
|
||||
/* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
|
||||
frag6_freef(head->ip6q_next, i);
|
||||
frag6_freef(head->ip6q_next, i, true);
|
||||
}
|
||||
IP6Q_UNLOCK(i);
|
||||
}
|
||||
@ -940,6 +940,45 @@ frag6_drain(void)
|
||||
VNET_LIST_RUNLOCK_NOSLEEP();
|
||||
}
|
||||
|
||||
/*
|
||||
* Drain off all datagram fragments belonging to
|
||||
* the given network interface.
|
||||
*/
|
||||
static void
|
||||
frag6_cleanup(void *arg __unused, struct ifnet *ifp)
|
||||
{
|
||||
struct ip6q *q6, *q6n, *head;
|
||||
struct ip6asfrag *af6;
|
||||
struct mbuf *m;
|
||||
int i;
|
||||
|
||||
KASSERT(ifp != NULL, ("%s: ifp is NULL", __func__));
|
||||
|
||||
CURVNET_SET_QUIET(ifp->if_vnet);
|
||||
for (i = 0; i < IP6REASS_NHASH; i++) {
|
||||
IP6Q_LOCK(i);
|
||||
head = IP6Q_HEAD(i);
|
||||
/* Scan fragment list. */
|
||||
for (q6 = head->ip6q_next; q6 != head; q6 = q6n) {
|
||||
q6n = q6->ip6q_next;
|
||||
|
||||
for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
|
||||
af6 = af6->ip6af_down) {
|
||||
m = IP6_REASS_MBUF(af6);
|
||||
|
||||
if (m->m_pkthdr.rcvif == ifp) {
|
||||
IP6STAT_INC(ip6s_fragdropped);
|
||||
frag6_freef(q6, i, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
IP6Q_UNLOCK(i);
|
||||
}
|
||||
CURVNET_RESTORE();
|
||||
}
|
||||
EVENTHANDLER_DEFINE(ifnet_departure_event, frag6_cleanup, NULL, 0);
|
||||
|
||||
int
|
||||
ip6_deletefraghdr(struct mbuf *m, int offset, int wait)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user