diff --git a/sys/contrib/ck/include/ck_queue.h b/sys/contrib/ck/include/ck_queue.h --- a/sys/contrib/ck/include/ck_queue.h +++ b/sys/contrib/ck/include/ck_queue.h @@ -321,6 +321,22 @@ } \ } while (0) +#define CK_STAILQ_REMOVE_CHECK(head, elm, type, field, found) do { \ + (found) = true; \ + if ((head)->cstqh_first == (elm)) { \ + CK_STAILQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->cstqh_first; \ + while (curelm->field.cstqe_next != (elm) && \ + curelm->field.cstqe_next) \ + curelm = curelm->field.cstqe_next; \ + if (curelm->field.cstqe_next) \ + CK_STAILQ_REMOVE_AFTER(head, curelm, field); \ + else \ + (found) = false; \ + } \ +} while (0) + #define CK_STAILQ_REMOVE_AFTER(head, elm, field) do { \ ck_pr_store_ptr(&(elm)->field.cstqe_next, \ (elm)->field.cstqe_next->field.cstqe_next); \ diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -1442,11 +1442,14 @@ { char ip6buf[INET6_ADDRSTRLEN]; int remove_lle; + bool ifa_found; IF_ADDR_WLOCK(ifp); - CK_STAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifaddr, ifa_link); + CK_STAILQ_REMOVE_CHECK(&ifp->if_addrhead, &ia->ia_ifa, ifaddr, ifa_link, + ifa_found); IF_ADDR_WUNLOCK(ifp); - ifa_free(&ia->ia_ifa); /* if_addrhead */ + if (ifa_found) + ifa_free(&ia->ia_ifa); /* if_addrhead */ /* * Defer the release of what might be the last reference to the @@ -1454,7 +1457,8 @@ * cleanup. */ IN6_IFADDR_WLOCK(); - CK_STAILQ_REMOVE(&V_in6_ifaddrhead, ia, in6_ifaddr, ia_link); + CK_STAILQ_REMOVE_CHECK(&V_in6_ifaddrhead, ia, in6_ifaddr, ia_link, + ifa_found); CK_LIST_REMOVE(ia, ia6_hash); IN6_IFADDR_WUNLOCK(); @@ -1485,7 +1489,8 @@ if ((ia->ia6_flags & IN6_IFF_AUTOCONF)) { pfxlist_onlink_check(); } - ifa_free(&ia->ia_ifa); /* in6_ifaddrhead */ + if (ifa_found) + ifa_free(&ia->ia_ifa); /* in6_ifaddrhead */ } /* @@ -1571,10 +1576,8 @@ WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "Invoking IPv6 network device address event may sleep"); - ifa_ref(&ia->ia_ifa); EVENTHANDLER_INVOKE(ifaddr_event_ext, ifp, &ia->ia_ifa, IFADDR_EVENT_ADD); - ifa_free(&ia->ia_ifa); return (error); }