[Openvpn-devel,RFC,ovpn,net] ovpn: fix use after free in unlock_ovpn()

Message ID 20260527113954.3592539-1-marco@mandelbit.com
State New
Headers
Series [Openvpn-devel,RFC,ovpn,net] ovpn: fix use after free in unlock_ovpn() |

Commit Message

Marco Baffo May 27, 2026, 11:39 a.m. UTC
  unlock_ovpn() iterates over the release_list using llist_for_each_entry()
and drops the peer reference inside the loop body via ovpn_peer_put().

If this drops the last reference, the peer is eventually freed. However,
llist_for_each_entry() reads peer->release_entry.next in the loop advance
expression, which runs after the body. By that time the peer may have
already been freed, resulting in a use after free when advancing to the
next list entry.

Fix this by using llist_for_each_entry_safe(), which caches the next
pointer before executing the loop body.

Signed-off-by: Marco Baffo <marco@mandelbit.com>
---
 drivers/net/ovpn/peer.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
  

Comments

Antonio Quartulli June 8, 2026, 1:35 p.m. UTC | #1
Hi Marco,

On 27/05/2026 13:39, Marco Baffo wrote:
> unlock_ovpn() iterates over the release_list using llist_for_each_entry()
> and drops the peer reference inside the loop body via ovpn_peer_put().
> 
> If this drops the last reference, the peer is eventually freed. However,
> llist_for_each_entry() reads peer->release_entry.next in the loop advance
> expression, which runs after the body. By that time the peer may have
> already been freed, resulting in a use after free when advancing to the
> next list entry.
> 
> Fix this by using llist_for_each_entry_safe(), which caches the next
> pointer before executing the loop body.
> 
> Signed-off-by: Marco Baffo <marco@mandelbit.com>

Nice fix. Please send this as PATCH as well.

Regards,

> ---
>   drivers/net/ovpn/peer.c | 4 ++--
>   1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
> index c02dfab51a6e..ff7c6ce9fcad 100644
> --- a/drivers/net/ovpn/peer.c
> +++ b/drivers/net/ovpn/peer.c
> @@ -26,11 +26,11 @@ 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);
>   	}
  

Patch

diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
index c02dfab51a6e..ff7c6ce9fcad 100644
--- a/drivers/net/ovpn/peer.c
+++ b/drivers/net/ovpn/peer.c
@@ -26,11 +26,11 @@  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);
 	}