Index: in6.c =================================================================== --- in6.c (revision 250967) +++ in6.c (working copy) @@ -738,7 +738,7 @@ /* relate the address to the prefix */ if (ia->ia6_ndpr == NULL) { ia->ia6_ndpr = pr; - pr->ndpr_refcnt++; + atomic_add_int(&pr->ndpr_refcnt, 1); /* * If this is the first autoconf address from the @@ -803,11 +803,22 @@ * as much backward compatibility as possible in terms of * the ioctl operation. * Note that in6_purgeaddr() will decrement ndpr_refcnt. + * + * We're taking a (temporary) reference here, so make sure to + * bump the refcount and only call prelist_remove() if we end up + * holding the last reference after in6_purgeaddr() returns. + * This prevents a potential leak if two threads race to delete + * the same address, and a potential double free if two threads + * race to delete distinct addresses with the same prefix. */ pr = ia->ia6_ndpr; + if (pr != NULL) + atomic_add_int(&pr->ndpr_refcnt, 1); in6_purgeaddr(&ia->ia_ifa); - if (pr && pr->ndpr_refcnt == 0) + if (pr != NULL && + atomic_fetchadd_int(&pr->ndpr_refcnt, -1) == 1) prelist_remove(pr); + pr = NULL; EVENTHANDLER_INVOKE(ifaddr_event, ifp); break; } @@ -1561,7 +1572,7 @@ "in6_unlink_ifa: autoconf'ed address " "%p has no prefix\n", ia)); } else { - ia->ia6_ndpr->ndpr_refcnt--; + atomic_add_int(&ia->ia6_ndpr->ndpr_refcnt, -1); ia->ia6_ndpr = NULL; } Index: nd6_rtr.c =================================================================== --- nd6_rtr.c (revision 250967) +++ nd6_rtr.c (working copy) @@ -1246,7 +1246,7 @@ /* * note that we should use pr (not new) for reference. */ - pr->ndpr_refcnt++; + atomic_add_int(&pr->ndpr_refcnt, 1); ia6->ia6_ndpr = pr; /* @@ -2026,7 +2026,7 @@ return (EINVAL); /* XXX */ } newia->ia6_ndpr = ia0->ia6_ndpr; - newia->ia6_ndpr->ndpr_refcnt++; + atomic_add_int(&newia->ia6_ndpr->ndpr_refcnt, 1); ifa_free(&newia->ia_ifa); /*