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
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -531,6 +531,8 @@ struct in6_ifextra {
|
||||
} scope6_id;
|
||||
|
||||
struct lltable *lltable;
|
||||
|
||||
struct epoch_context epoch_ctx;
|
||||
};
|
||||
|
||||
#define LLTABLE6(ifp) ((ifp)->if_inet6->lltable)
|
||||
|
||||
Reference in New Issue
Block a user