Download raw body.
use after free in nd6 dad
Hi,
In IPv6 neghbor discovery, the duplicate address detection code
could trigger a use after free. I have seen a data modifed on
freelist panic. It looks like the timeout field of struct dadq.
nd6_dad_stop() calls nd6_dad_stoptimer() and nd6_dad_destroy()
without waiting for the timer to run before freeing. I don't want
to use a barrier in IP input packet path. Both this code and the
timer runs with exclusive netlock which does not work with barriers.
Refcounting struct dadq feels like overkill. So I implemented a
reaper on the timeout queue.
While there remove a useless NULL check in nd6_dad_timer().
ok?
bluhm
Index: netinet6/nd6_nbr.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/nd6_nbr.c,v
diff -u -p -r1.162 nd6_nbr.c
--- netinet6/nd6_nbr.c 23 Jul 2025 22:32:49 -0000 1.162
+++ netinet6/nd6_nbr.c 11 Sep 2025 18:49:24 -0000
@@ -73,6 +73,7 @@ struct dadq {
struct dadq *nd6_dad_find(struct ifaddr *);
void nd6_dad_destroy(struct dadq *);
+void nd6_dad_reaper(void *);
void nd6_dad_starttimer(struct dadq *);
void nd6_dad_stoptimer(struct dadq *);
void nd6_dad_timer(void *);
@@ -994,9 +995,18 @@ void
nd6_dad_destroy(struct dadq *dp)
{
TAILQ_REMOVE(&dadq, dp, dad_list);
+ ip6_dad_pending--;
+ timeout_set_proc(&dp->dad_timer_ch, nd6_dad_reaper, dp);
+ timeout_add(&dp->dad_timer_ch, 0);
+}
+
+void
+nd6_dad_reaper(void *arg)
+{
+ struct dadq *dp = arg;
+
ifafree(dp->dad_ifa);
free(dp, M_IP6NDP, sizeof(*dp));
- ip6_dad_pending--;
}
void
@@ -1089,9 +1099,9 @@ nd6_dad_stop(struct ifaddr *ifa)
}
void
-nd6_dad_timer(void *xifa)
+nd6_dad_timer(void *arg)
{
- struct ifaddr *ifa;
+ struct ifaddr *ifa = arg;
struct in6_ifaddr *ia6;
struct in6_addr daddr6, taddr6;
struct ifnet *ifp;
@@ -1100,12 +1110,6 @@ nd6_dad_timer(void *xifa)
NET_LOCK();
- /* Sanity check */
- if (xifa == NULL) {
- log(LOG_ERR, "%s: called with null parameter\n", __func__);
- goto done;
- }
- ifa = xifa;
ia6 = ifatoia6(ifa);
taddr6 = ia6->ia_addr.sin6_addr;
ifp = ifa->ifa_ifp;
use after free in nd6 dad