Included fixes:

* ensure keepalive timestamps are computed using monotonic source
 * avoid UAF in unlock_ovpn() when iterating over release_list
 * fix memleak in selftest tool
 * ensure reference to peer is acquired before scheduling worker
   (which may drop the not-yet-taken ref)
 * fix refcount leak in case of concurrent TX and RX TCP error
 * fix potential refcount unbalance in case of sock release in
   P2P mode
 -----BEGIN PGP SIGNATURE-----
 
 iJEEABYIADkWIQQr0db7q+Rc7Zog28Fc8QQzwdnOtwUCal4xNRsUgAAAAAAEAA5t
 YW51MiwyLjUrMS4xMiwyLDIACgkQXPEEM8HZzrcjWQEAzhUfvdvKlB0R3IjYs5ze
 xUocdtTNVf3B2mOzRDMeu9oA/0PlyHorMDIi5l4ffiz+yzqbsul3929KjsRwTWnd
 E8gI
 =sQH6
 -----END PGP SIGNATURE-----

Merge tag 'ovpn-net-20260720' of https://github.com/OpenVPN/ovpn-net-next

Antonio Quartulli says:

====================
Included fixes:
* ensure keepalive timestamps are computed using monotonic source
* avoid UAF in unlock_ovpn() when iterating over release_list
* fix memleak in selftest tool
* ensure reference to peer is acquired before scheduling worker
  (which may drop the not-yet-taken ref)
* fix refcount leak in case of concurrent TX and RX TCP error
* fix potential refcount unbalance in case of sock release in
  P2P mode

* tag 'ovpn-net-20260720' of https://github.com/OpenVPN/ovpn-net-next:
  ovpn: use monotonic clock for peer keepalive timeouts
  ovpn: fix use after free in unlock_ovpn()
  selftests/net: ovpn: fix getaddrinfo memory leak in ovpn_parse_remote()
  ovpn: hold peer before scheduling keepalive work
  ovpn: fix peer refcount leak in TCP error paths
  ovpn: avoid putting unrelated P2P peer on socket release
====================

Link: https://patch.msgid.link/20260720144131.3657121-1-antonio@openvpn.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-07-23 10:04:16 -07:00
commit d73a2e81f3
4 changed files with 18 additions and 12 deletions

View File

@ -142,7 +142,7 @@ void ovpn_decrypt_post(void *data, int ret)
}
/* keep track of last received authenticated packet for keepalive */
WRITE_ONCE(peer->last_recv, ktime_get_real_seconds());
WRITE_ONCE(peer->last_recv, ktime_get_boottime_seconds());
rcu_read_lock();
sock = rcu_dereference(peer->sock);
@ -294,7 +294,7 @@ void ovpn_encrypt_post(void *data, int ret)
ovpn_peer_stats_increment_tx(&peer->link_stats, orig_len);
/* keep track of last sent packet for keepalive */
WRITE_ONCE(peer->last_sent, ktime_get_real_seconds());
WRITE_ONCE(peer->last_sent, ktime_get_boottime_seconds());
/* skb passed down the stack - don't free it */
skb = NULL;
err_unlock:

View File

@ -26,11 +26,12 @@ static void unlock_ovpn(struct ovpn_priv *ovpn,
struct llist_head *release_list)
__releases(&ovpn->lock)
{
struct ovpn_peer *peer;
struct ovpn_peer *peer, *next;
spin_unlock_bh(&ovpn->lock);
llist_for_each_entry(peer, release_list->first, release_entry) {
llist_for_each_entry_safe(peer, next, release_list->first,
release_entry) {
ovpn_socket_release(peer);
ovpn_peer_put(peer);
}
@ -44,7 +45,7 @@ static void unlock_ovpn(struct ovpn_priv *ovpn,
*/
void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout)
{
time64_t now = ktime_get_real_seconds();
time64_t now = ktime_get_boottime_seconds();
netdev_dbg(peer->ovpn->dev,
"scheduling keepalive for peer %u: interval=%u timeout=%u\n",
@ -1167,7 +1168,6 @@ static void ovpn_peer_release_p2p(struct ovpn_priv *ovpn, struct sock *sk,
ovpn_sock = rcu_access_pointer(peer->sock);
if (!ovpn_sock || ovpn_sock->sk != sk) {
spin_unlock_bh(&ovpn->lock);
ovpn_peer_put(peer);
return;
}
}
@ -1285,8 +1285,10 @@ static time64_t ovpn_peer_keepalive_work_single(struct ovpn_peer *peer,
netdev_dbg(peer->ovpn->dev,
"sending keepalive to peer %u\n",
peer->id);
if (schedule_work(&peer->keepalive_work))
ovpn_peer_hold(peer);
if (WARN_ON(!ovpn_peer_hold(peer)))
return 0;
if (!schedule_work(&peer->keepalive_work))
ovpn_peer_put(peer);
}
if (next_run1 < next_run2)
@ -1357,7 +1359,7 @@ void ovpn_peer_keepalive_work(struct work_struct *work)
{
struct ovpn_priv *ovpn = container_of(work, struct ovpn_priv,
keepalive_work.work);
time64_t next_run = 0, now = ktime_get_real_seconds();
time64_t next_run = 0, now = ktime_get_boottime_seconds();
LLIST_HEAD(release_list);
spin_lock_bh(&ovpn->lock);

View File

@ -151,7 +151,8 @@ static void ovpn_tcp_rcv(struct strparser *strp, struct sk_buff *skb)
/* take reference for deferred peer deletion. should never fail */
if (WARN_ON(!ovpn_peer_hold(peer)))
goto err_nopeer;
schedule_work(&peer->tcp.defer_del_work);
if (!schedule_work(&peer->tcp.defer_del_work))
ovpn_peer_put(peer);
ovpn_dev_dstats_rx_dropped(peer->ovpn->dev);
err_nopeer:
kfree_skb(skb);
@ -283,7 +284,8 @@ static void ovpn_tcp_send_sock(struct ovpn_peer *peer, struct sock *sk)
* stream therefore we abort the connection
*/
ovpn_peer_hold(peer);
schedule_work(&peer->tcp.defer_del_work);
if (!schedule_work(&peer->tcp.defer_del_work))
ovpn_peer_put(peer);
/* we bail out immediately and keep tx_in_progress set
* to true. This way we prevent more TX attempts

View File

@ -1785,7 +1785,7 @@ static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host,
const char *service, const char *vpnip)
{
int ret;
struct addrinfo *result;
struct addrinfo *result = NULL;
struct addrinfo hints = {
.ai_family = ovpn->sa_family,
.ai_socktype = SOCK_DGRAM,
@ -1809,6 +1809,8 @@ static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host,
}
memcpy(&ovpn->remote, result->ai_addr, result->ai_addrlen);
freeaddrinfo(result);
result = NULL;
}
if (vpnip) {