if_ovpn: use epoch to free peers

Avoid a possible use-after-free in the rx path.
ovpn_decrypt_rx_cb() calls ovpn_finish_rx() which releases the lock,
but continues to use the peer.
Ensure that the peer cannot be freed until we're sure all potential
users have stopped using it (i.e. have left net_epoch).

Reported by:	Kevin Day <kevin@your.org>
MFC after:	1 week
Sponsored by:	Rubicon Communications, LLC ("Netgate")
This commit is contained in:
Kristof Provost
2025-12-09 11:55:30 +01:00
parent 66129def7b
commit 5e2bbfe387
+12 -2
View File
@@ -161,6 +161,7 @@ struct ovpn_kpeer {
struct callout ping_rcv;
counter_u64_t counters[OVPN_PEER_COUNTER_SIZE];
struct epoch_context epoch_ctx;
};
struct ovpn_counters {
@@ -568,6 +569,15 @@ ovpn_notify_float(struct ovpn_softc *sc, uint32_t peerid,
return (0);
}
static void
_ovpn_free_peer(struct epoch_context *ctx) {
struct ovpn_kpeer *peer = __containerof(ctx, struct ovpn_kpeer,
epoch_ctx);
uma_zfree_pcpu(pcpu_zone_4, peer->last_active);
free(peer, M_OVPN);
}
static void
ovpn_peer_release_ref(struct ovpn_kpeer *peer, bool locked)
{
@@ -606,8 +616,8 @@ ovpn_peer_release_ref(struct ovpn_kpeer *peer, bool locked)
callout_stop(&peer->ping_send);
callout_stop(&peer->ping_rcv);
uma_zfree_pcpu(pcpu_zone_4, peer->last_active);
free(peer, M_OVPN);
NET_EPOCH_CALL(_ovpn_free_peer, &peer->epoch_ctx);
if (! locked)
OVPN_WUNLOCK(sc);