diff --git a/sys/dev/wg/if_wg.c b/sys/dev/wg/if_wg.c --- a/sys/dev/wg/if_wg.c +++ b/sys/dev/wg/if_wg.c @@ -317,7 +317,7 @@ static void wg_aip_remove_all(struct wg_softc *, struct wg_peer *); static struct wg_peer *wg_peer_alloc(struct wg_softc *, const uint8_t [WG_KEY_SIZE]); static void wg_peer_free_deferred(struct noise_remote *); -static void wg_peer_destroy(struct wg_peer *); +static void wg_peer_destroy(struct wg_peer *, bool); static void wg_peer_destroy_all(struct wg_softc *); static void wg_peer_send_buf(struct wg_peer *, uint8_t *, size_t); static void wg_send_initiation(struct wg_peer *); @@ -451,7 +451,7 @@ } static void -wg_peer_destroy(struct wg_peer *peer) +wg_peer_destroy(struct wg_peer *peer, bool linked) { struct wg_softc *sc = peer->p_sc; sx_assert(&sc->sc_lock, SX_XLOCKED); @@ -468,8 +468,11 @@ /* Remove peer from the interface, then free. Some references may still * exist to p_remote, so noise_remote_free will wait until they're all * put to call wg_peer_free_deferred. */ - sc->sc_peers_num--; - TAILQ_REMOVE(&sc->sc_peers, peer, p_entry); + if (linked) { + sc->sc_peers_num--; + TAILQ_REMOVE(&sc->sc_peers, peer, p_entry); + } + DPRINTF(sc, "Peer %" PRIu64 " destroyed\n", peer->p_id); noise_remote_free(peer->p_remote, wg_peer_free_deferred); } @@ -479,7 +482,7 @@ { struct wg_peer *peer, *tpeer; TAILQ_FOREACH_SAFE(peer, &sc->sc_peers, p_entry, tpeer) - wg_peer_destroy(peer); + wg_peer_destroy(peer, true); } static void @@ -2398,7 +2401,7 @@ if (nvlist_exists_bool(nvl, "remove") && nvlist_get_bool(nvl, "remove")) { if (remote != NULL) { - wg_peer_destroy(peer); + wg_peer_destroy(peer, true); noise_remote_put(remote); } return (0); @@ -2481,8 +2484,14 @@ noise_remote_put(remote); return (0); out: - if (need_insert) /* If we fail, only destroy if it was new. */ - wg_peer_destroy(peer); + /* + * If we fail, only destroy if it was new. Linking the peer into the + * device's peer tailq and accounting for it in sc_peers_num only + * happens after every other plausible failure path, so if we got here + * then we should avoid those parts of wg_peer_destroy(). + */ + if (need_insert) + wg_peer_destroy(peer, false); if (remote != NULL) noise_remote_put(remote); return (err); @@ -2552,7 +2561,7 @@ if ((remote = noise_remote_lookup(sc->sc_local, public)) != NULL) { peer = noise_remote_arg(remote); - wg_peer_destroy(peer); + wg_peer_destroy(peer, true); noise_remote_put(remote); } }