git: 613acc64834b - main - mbuf: do not restore dying interfaces

From: Ed Maste <emaste_at_FreeBSD.org>
Date: Thu, 05 May 2022 19:14:53 UTC
The branch main has been updated by emaste:

URL: https://cgit.FreeBSD.org/src/commit/?id=613acc64834b17dd30527b488198b1a9139911c1

commit 613acc64834b17dd30527b488198b1a9139911c1
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2022-01-27 21:01:09 +0000
Commit:     Ed Maste <emaste@FreeBSD.org>
CommitDate: 2022-05-05 18:38:08 +0000

    mbuf: do not restore dying interfaces
    
    When we remove an interface it is first removed from the interface list
    V_ifnet (by if_unlink_ifnet()) and marked as IFF_DYING. We then wait for
    any possible references to stop being used (i.e.
    epoch_wait/epoch_drain_callbacks) before we tear it fully down.
    
    However, the index in ifindex_table is not removed, so m_rcvif_restore()
    can still find the (now dying) interface.
    
    This results in panics, for example when dummynet restores the rcvif
    pointer and passes a packet to ip6_input() we can panic because the
    AF_INET6 domain has already been removed (so we end up dereferencing a
    NULL pointer there).
    
    Check that the interface is not dying before we restore it, which is
    equivalent to checking its presence in V_ifnet, and thus ensures that
    future accesses (while in NET_EPOCH) are safe.
    
    Reviewed by:    glebius
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D34076
    
    (cherry picked from commit 703e533da5e2e4743d38bbf4605fec041bc69976)
---
 sys/kern/kern_mbuf.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/sys/kern/kern_mbuf.c b/sys/kern/kern_mbuf.c
index 5c69f663c0e2..23050e991418 100644
--- a/sys/kern/kern_mbuf.c
+++ b/sys/kern/kern_mbuf.c
@@ -1650,11 +1650,16 @@ m_rcvif_serialize(struct mbuf *m)
 struct ifnet *
 m_rcvif_restore(struct mbuf *m)
 {
+	struct ifnet *ifp;
 
 	M_ASSERTPKTHDR(m);
+	NET_EPOCH_ASSERT();
+
+	ifp = ifnet_byindexgen(m->m_pkthdr.rcvidx, m->m_pkthdr.rcvgen);
+	if (ifp == NULL || (ifp->if_flags & IFF_DYING))
+		return (NULL);
 
-	return ((m->m_pkthdr.rcvif = ifnet_byindexgen(m->m_pkthdr.rcvidx,
-	    m->m_pkthdr.rcvgen)));
+	return (m->m_pkthdr.rcvif = ifp);
 }
 
 /*