The branch main has been updated by glebius:

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

commit f3de667137e90679cd20fa5c1dcd93a4c51ad848
Author:     Gleb Smirnoff <[email protected]>
AuthorDate: 2026-01-23 22:18:18 +0000
Commit:     Gleb Smirnoff <[email protected]>
CommitDate: 2026-01-23 22:18:18 +0000

    netinet6: free in6_ifextra with epoch_call(9)
    
    This is expected to fix the old in6_selecthlim() panics.  The nature of
    the panic is that a packet sending thread will obtain the struct ifnet
    pointer locklessly and then pick the if_inet6 pointer from it and
    dereference it. While the struct ifnet is freed via epoch_call(9), the
    struct in6_ifextra until this change was not.  For the forwarded packets,
    or locally originated non-TCP packets we were probably safe due to the old
    if_dead trick.  But locally originated TCP packets may dereference
    in6_ifextra via direct call into in6_selecthlim() from the tcp_output(),
    before ip6_output().
    
    NB: hypothetically a similar problem also applies to IPv4's if_inet pointer,
    but there are no known panics, yet.
    
    PR:                     279653
    Reviewed by:            tuexen
    Differential Revision:  https://reviews.freebsd.org/D54728
---
 sys/netinet6/in6_ifattach.c | 25 ++++++++++++++++++++-----
 sys/netinet6/in6_var.h      |  2 ++
 2 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c
index b9ce64e5b86b..c38fe90632f8 100644
--- a/sys/netinet6/in6_ifattach.c
+++ b/sys/netinet6/in6_ifattach.c
@@ -899,6 +899,19 @@ in6_ifdetach(struct ifnet *ifp)
        _in6_ifdetach(ifp, 1);
 }
 
+static void
+in6_ifextra_free(epoch_context_t ctx)
+{
+       struct in6_ifextra *ext =
+            __containerof(ctx, struct in6_ifextra, epoch_ctx);
+
+       COUNTER_ARRAY_FREE(ext->in6_ifstat,
+           sizeof(struct in6_ifstat) / sizeof(uint64_t));
+       COUNTER_ARRAY_FREE(ext->icmp6_ifstat,
+           sizeof(struct icmp6_ifstat) / sizeof(uint64_t));
+       free(ext, M_IFADDR);
+}
+
 static void
 in6_ifdeparture(void *arg __unused, struct ifnet *ifp)
 {
@@ -916,14 +929,16 @@ in6_ifdeparture(void *arg __unused, struct ifnet *ifp)
        if (!VNET_IS_SHUTTING_DOWN(ifp->if_vnet))
 #endif
                _in6_ifdetach(ifp, 1);
+       /*
+        * XXXGL: mld and nd bits are left in a consistent state after
+        * destructors, but I'm not sure if it safe to call lltable_free() here.
+        * Individual lle entries are epoch(9) protected, but the table itself
+        * isn't.
+        */
        mld_domifdetach(ifp);
        nd6_ifdetach(ifp);
        lltable_free(ext->lltable);
-       COUNTER_ARRAY_FREE(ext->in6_ifstat,
-           sizeof(struct in6_ifstat) / sizeof(uint64_t));
-       COUNTER_ARRAY_FREE(ext->icmp6_ifstat,
-           sizeof(struct icmp6_ifstat) / sizeof(uint64_t));
-       free(ext, M_IFADDR);
+       NET_EPOCH_CALL(in6_ifextra_free, &ext->epoch_ctx);
 }
 EVENTHANDLER_DEFINE(ifnet_departure_event, in6_ifdeparture, NULL,
     EVENTHANDLER_PRI_ANY);
diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h
index 894628b796d1..057cd84b6ea7 100644
--- a/sys/netinet6/in6_var.h
+++ b/sys/netinet6/in6_var.h
@@ -531,6 +531,8 @@ struct in6_ifextra {
        } scope6_id;
 
        struct lltable *lltable;
+
+       struct epoch_context    epoch_ctx;
 };
 
 #define        LLTABLE6(ifp)   ((ifp)->if_inet6->lltable)

Reply via email to