]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ovpn: use datagram_poll_queue for socket readiness in TCP
authorRalf Lici <ralf@mandelbit.com>
Tue, 21 Oct 2025 10:09:42 +0000 (12:09 +0200)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 23 Oct 2025 13:46:04 +0000 (15:46 +0200)
openvpn TCP encapsulation uses a custom queue to deliver packets to
userspace. Currently it relies on datagram_poll, which checks
sk_receive_queue, leading to false readiness signals when that queue
contains non-userspace packets.

Switch ovpn_tcp_poll to use datagram_poll_queue with the peer's
user_queue, ensuring poll only signals readiness when userspace data is
actually available. Also refactor ovpn_tcp_poll in order to enforce the
assumption we can make on the lifetime of ovpn_sock and peer.

Fixes: 11851cbd60ea ("ovpn: implement TCP transport")
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
Signed-off-by: Ralf Lici <ralf@mandelbit.com>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Link: https://patch.msgid.link/20251021100942.195010-4-ralf@mandelbit.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/ovpn/tcp.c

index 289f62c5d2c70061cb5281d99480f5252cfcc4a4..0d7f30360d8746b53f6b75770f0a27f522de9233 100644 (file)
@@ -560,16 +560,34 @@ static void ovpn_tcp_close(struct sock *sk, long timeout)
 static __poll_t ovpn_tcp_poll(struct file *file, struct socket *sock,
                              poll_table *wait)
 {
-       __poll_t mask = datagram_poll(file, sock, wait);
+       struct sk_buff_head *queue = &sock->sk->sk_receive_queue;
        struct ovpn_socket *ovpn_sock;
+       struct ovpn_peer *peer = NULL;
+       __poll_t mask;
 
        rcu_read_lock();
        ovpn_sock = rcu_dereference_sk_user_data(sock->sk);
-       if (ovpn_sock && ovpn_sock->peer &&
-           !skb_queue_empty(&ovpn_sock->peer->tcp.user_queue))
-               mask |= EPOLLIN | EPOLLRDNORM;
+       /* if we landed in this callback, we expect to have a
+        * meaningful state. The ovpn_socket lifecycle would
+        * prevent it otherwise.
+        */
+       if (WARN(!ovpn_sock || !ovpn_sock->peer,
+                "ovpn: null state in ovpn_tcp_poll!")) {
+               rcu_read_unlock();
+               return 0;
+       }
+
+       if (ovpn_peer_hold(ovpn_sock->peer)) {
+               peer = ovpn_sock->peer;
+               queue = &peer->tcp.user_queue;
+       }
        rcu_read_unlock();
 
+       mask = datagram_poll_queue(file, sock, wait, queue);
+
+       if (peer)
+               ovpn_peer_put(peer);
+
        return mask;
 }