netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Sabrina Dubroca <sd@queasysnail.net>
To: Antonio Quartulli <antonio@openvpn.net>
Cc: Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Donald Hunter <donald.hunter@gmail.com>,
	Shuah Khan <shuah@kernel.org>,
	ryazanov.s.a@gmail.com, Andrew Lunn <andrew@lunn.ch>,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-kselftest@vger.kernel.org
Subject: Re: [PATCH net-next v11 12/23] ovpn: implement TCP transport
Date: Thu, 31 Oct 2024 16:25:47 +0100	[thread overview]
Message-ID: <ZyOhe3yKTiqCF2TH@hog> (raw)
In-Reply-To: <20241029-b4-ovpn-v11-12-de4698c73a25@openvpn.net>

2024-10-29, 11:47:25 +0100, Antonio Quartulli wrote:
> +static void ovpn_socket_release_work(struct work_struct *work)
> +{
> +	struct ovpn_socket *sock = container_of(work, struct ovpn_socket, work);
> +
> +	ovpn_socket_detach(sock->sock);
> +	kfree_rcu(sock, rcu);
> +}
> +
> +static void ovpn_socket_schedule_release(struct ovpn_socket *sock)
> +{
> +	INIT_WORK(&sock->work, ovpn_socket_release_work);
> +	schedule_work(&sock->work);

How does module unloading know that it has to wait for this work to
complete? Will ovpn_cleanup get stuck until some refcount gets
released by this work?


[...]
> +static void ovpn_tcp_rcv(struct strparser *strp, struct sk_buff *skb)
> +{
> +	struct ovpn_peer *peer = container_of(strp, struct ovpn_peer, tcp.strp);
> +	struct strp_msg *msg = strp_msg(skb);
> +	size_t pkt_len = msg->full_len - 2;
> +	size_t off = msg->offset + 2;
> +
> +	/* ensure skb->data points to the beginning of the openvpn packet */
> +	if (!pskb_pull(skb, off)) {
> +		net_warn_ratelimited("%s: packet too small\n",
> +				     peer->ovpn->dev->name);
> +		goto err;
> +	}
> +
> +	/* strparser does not trim the skb for us, therefore we do it now */
> +	if (pskb_trim(skb, pkt_len) != 0) {
> +		net_warn_ratelimited("%s: trimming skb failed\n",
> +				     peer->ovpn->dev->name);
> +		goto err;
> +	}
> +
> +	/* we need the first byte of data to be accessible
> +	 * to extract the opcode and the key ID later on
> +	 */
> +	if (!pskb_may_pull(skb, 1)) {
> +		net_warn_ratelimited("%s: packet too small to fetch opcode\n",
> +				     peer->ovpn->dev->name);
> +		goto err;
> +	}
> +
> +	/* DATA_V2 packets are handled in kernel, the rest goes to user space */
> +	if (likely(ovpn_opcode_from_skb(skb, 0) == OVPN_DATA_V2)) {
> +		/* hold reference to peer as required by ovpn_recv().
> +		 *
> +		 * NOTE: in this context we should already be holding a
> +		 * reference to this peer, therefore ovpn_peer_hold() is
> +		 * not expected to fail
> +		 */
> +		if (WARN_ON(!ovpn_peer_hold(peer)))
> +			goto err;
> +
> +		ovpn_recv(peer, skb);
> +	} else {
> +		/* The packet size header must be there when sending the packet
> +		 * to userspace, therefore we put it back
> +		 */
> +		skb_push(skb, 2);
> +		ovpn_tcp_to_userspace(peer, strp->sk, skb);
> +	}
> +
> +	return;
> +err:
> +	netdev_err(peer->ovpn->dev,
> +		   "cannot process incoming TCP data for peer %u\n", peer->id);

This should also be ratelimited, and maybe just combined with the
net_warn_ratelimited just before each goto.

> +	dev_core_stats_rx_dropped_inc(peer->ovpn->dev);
> +	kfree_skb(skb);
> +	ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_TRANSPORT_ERROR);
> +}

[...]
> +void ovpn_tcp_socket_detach(struct socket *sock)
> +{
[...]
> +	/* restore CBs that were saved in ovpn_sock_set_tcp_cb() */
> +	sock->sk->sk_data_ready = peer->tcp.sk_cb.sk_data_ready;
> +	sock->sk->sk_write_space = peer->tcp.sk_cb.sk_write_space;
> +	sock->sk->sk_prot = peer->tcp.sk_cb.prot;
> +	sock->sk->sk_socket->ops = peer->tcp.sk_cb.ops;
> +	rcu_assign_sk_user_data(sock->sk, NULL);
> +
> +	rcu_read_unlock();
> +
> +	/* cancel any ongoing work. Done after removing the CBs so that these
> +	 * workers cannot be re-armed
> +	 */

I'm not sure whether a barrier is needed to prevent compiler/CPU
reordering here.

> +	cancel_work_sync(&peer->tcp.tx_work);
> +	strp_done(&peer->tcp.strp);
> +}
> +
> +static void ovpn_tcp_send_sock(struct ovpn_peer *peer)
> +{
> +	struct sk_buff *skb = peer->tcp.out_msg.skb;
> +
> +	if (!skb)
> +		return;
> +
> +	if (peer->tcp.tx_in_progress)
> +		return;
> +
> +	peer->tcp.tx_in_progress = true;

Sorry, I never answered your question about my concerns in a previous
review here.

We can reach ovpn_tcp_send_sock in two different contexts:
 - lock_sock (either from ovpn_tcp_sendmsg or ovpn_tcp_tx_work)
 - bh_lock_sock (from ovpn_tcp_send_skb, ie "data path")

These are not fully mutually exclusive. lock_sock grabs bh_lock_sock
(a spinlock) for a brief period to mark the (sleeping/mutex) lock as
taken, and then releases it.

So when bh_lock_sock is held, it's not possible to grab lock_sock. But
when lock_sock is taken, it's still possible to grab bh_lock_sock.


The buggy scenario would be:

  (data path encrypt)                       (sendmsg)
  ovpn_tcp_send_skb                         lock_sock
                                              bh_lock_sock + owned=1 + bh_unlock_sock
  bh_lock_sock
  ovpn_tcp_send_sock_skb                      ovpn_tcp_send_sock_skb
    !peer->tcp.out_msg.skb                      !peer->tcp.out_msg.skb
    peer->tcp.out_msg.skb = ...                 peer->tcp.out_msg.skb = ...
    ovpn_tcp_send_sock                          ovpn_tcp_send_sock
      !peer->tcp.tx_in_progress                   !peer->tcp.tx_in_progress
      peer->tcp.tx_in_progress = true             peer->tcp.tx_in_progress = true
      // proceed                                  // proceed


That's 2 similar races, one on out_msg.skb and one on tx_in_progress.
It's a bit unlikely (but not impossible) that we'll have 2 cpus trying
to call skb_send_sock_locked at the same time, but if they just
overwrite each other's skb/len it's already pretty bad. The end of
ovpn_tcp_send_sock might also reset peer->tcp.out_msg.* just as
ovpn_tcp_send_skb -> ovpn_tcp_send_sock_skb starts setting it up
(peer->tcp.out_msg.skb gets cleared, ovpn_tcp_send_sock_skb proceeds
and sets skb+len, then maybe len gets reset to 0 by
ovpn_tcp_send_sock).


To avoid this problem, esp_output_tcp_finish (net/ipv4/esp4.c) does:

	bh_lock_sock(sk);
	if (sock_owned_by_user(sk))
		err = espintcp_queue_out(sk, skb);
	else
		err = espintcp_push_skb(sk, skb);
	bh_unlock_sock(sk);


(espintcp_push_skb is roughly equivalent to ovpn_tcp_send_sock_skb)


> +	do {
> +		int ret = skb_send_sock_locked(peer->sock->sock->sk, skb,
> +					       peer->tcp.out_msg.offset,
> +					       peer->tcp.out_msg.len);
> +		if (unlikely(ret < 0)) {
> +			if (ret == -EAGAIN)
> +				goto out;
> +
> +			net_warn_ratelimited("%s: TCP error to peer %u: %d\n",
> +					     peer->ovpn->dev->name, peer->id,
> +					     ret);
> +
> +			/* in case of TCP error we can't recover the VPN
> +			 * stream therefore we abort the connection
> +			 */
> +			ovpn_peer_del(peer,
> +				      OVPN_DEL_PEER_REASON_TRANSPORT_ERROR);
> +			break;
> +		}
> +
> +		peer->tcp.out_msg.len -= ret;
> +		peer->tcp.out_msg.offset += ret;
> +	} while (peer->tcp.out_msg.len > 0);
> +
> +	if (!peer->tcp.out_msg.len)
> +		dev_sw_netstats_tx_add(peer->ovpn->dev, 1, skb->len);
> +
> +	kfree_skb(peer->tcp.out_msg.skb);
> +	peer->tcp.out_msg.skb = NULL;
> +	peer->tcp.out_msg.len = 0;
> +	peer->tcp.out_msg.offset = 0;
> +
> +out:
> +	peer->tcp.tx_in_progress = false;
> +}
> +
> +static void ovpn_tcp_tx_work(struct work_struct *work)
> +{
> +	struct ovpn_peer *peer;
> +
> +	peer = container_of(work, struct ovpn_peer, tcp.tx_work);
> +
> +	lock_sock(peer->sock->sock->sk);
> +	ovpn_tcp_send_sock(peer);
> +	release_sock(peer->sock->sock->sk);
> +}
> +
> +void ovpn_tcp_send_sock_skb(struct ovpn_peer *peer, struct sk_buff *skb)
> +{
> +	if (peer->tcp.out_msg.skb)
> +		return;

That's leaking the skb? (and not counting the drop)

> +
> +	peer->tcp.out_msg.skb = skb;
> +	peer->tcp.out_msg.len = skb->len;
> +	peer->tcp.out_msg.offset = 0;
> +
> +	ovpn_tcp_send_sock(peer);
> +}
> +
> +static int ovpn_tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
> +{
[...]
> +	ret = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, size);
> +	if (ret) {
> +		kfree_skb(skb);
> +		net_err_ratelimited("%s: skb copy from iter failed: %d\n",
> +				    sock->peer->ovpn->dev->name, ret);
> +		goto unlock;
> +	}
> +
> +	ovpn_tcp_send_sock_skb(sock->peer, skb);

If we didn't send the packet (because one was already queued/in
progress), we should either stash it, or tell userspace that it wasn't
sent and it should retry later.

> +	ret = size;
> +unlock:
> +	release_sock(sk);
> +peer_free:
> +	ovpn_peer_put(peer);
> +	return ret;
> +}

-- 
Sabrina

  parent reply	other threads:[~2024-10-31 15:25 UTC|newest]

Thread overview: 158+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-10-29 10:47 [PATCH net-next v11 00/23] Introducing OpenVPN Data Channel Offload Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 01/23] netlink: add NLA_POLICY_MAX_LEN macro Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 02/23] net: introduce OpenVPN Data Channel Offload (ovpn) Antonio Quartulli
2024-11-06  0:31   ` Sergey Ryazanov
2024-11-15  9:56     ` Antonio Quartulli
2024-11-19  1:49       ` Sergey Ryazanov
2024-10-29 10:47 ` [PATCH net-next v11 03/23] ovpn: add basic netlink support Antonio Quartulli
2024-11-08 23:15   ` Sergey Ryazanov
2024-11-15 10:05     ` Antonio Quartulli
2024-11-19  2:05       ` Sergey Ryazanov
2024-11-19  8:12         ` Antonio Quartulli
2024-11-08 23:31   ` Sergey Ryazanov
2024-11-15 10:19     ` Antonio Quartulli
2024-11-19  2:23       ` Sergey Ryazanov
2024-11-19  8:16         ` Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 04/23] ovpn: add basic interface creation/destruction/management routines Antonio Quartulli
2024-11-09  1:01   ` Sergey Ryazanov
2024-11-12 16:47     ` Sabrina Dubroca
2024-11-12 23:56       ` Sergey Ryazanov
2024-11-14  8:07       ` Antonio Quartulli
2024-11-14 22:57         ` Sergey Ryazanov
2024-11-15 13:45           ` Antonio Quartulli
2024-11-15 13:00     ` Antonio Quartulli
2024-11-10 20:42   ` Sergey Ryazanov
2024-11-15 14:03     ` Antonio Quartulli
2024-11-19  3:08       ` Sergey Ryazanov
2024-11-19  8:45         ` Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 05/23] ovpn: keep carrier always on Antonio Quartulli
2024-11-09  1:11   ` Sergey Ryazanov
2024-11-15 14:13     ` Antonio Quartulli
2024-11-20 22:56       ` Sergey Ryazanov
2024-11-21 21:17         ` Antonio Quartulli
2024-11-23 22:25           ` Sergey Ryazanov
2024-11-23 22:52             ` Antonio Quartulli
2024-11-25  2:26               ` Sergey Ryazanov
2024-11-25 13:07                 ` Antonio Quartulli
2024-11-25 21:32                   ` Sergey Ryazanov
2024-11-26  8:17                     ` Antonio Quartulli
2024-12-02 10:40                       ` Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 06/23] ovpn: introduce the ovpn_peer object Antonio Quartulli
2024-10-30 16:37   ` Sabrina Dubroca
2024-10-30 20:47     ` Antonio Quartulli
2024-11-05 13:12       ` Sabrina Dubroca
2024-11-12 10:12         ` Antonio Quartulli
2024-11-10 13:38   ` Sergey Ryazanov
2024-11-12 17:31     ` Sabrina Dubroca
2024-11-13  1:37       ` Sergey Ryazanov
2024-11-13 10:03         ` Sabrina Dubroca
2024-11-20 23:22           ` Sergey Ryazanov
2024-11-21 21:23             ` Antonio Quartulli
2024-11-23 21:05               ` Sergey Ryazanov
2024-11-10 19:52   ` Sergey Ryazanov
2024-11-14 14:55     ` Antonio Quartulli
2024-11-20 11:56   ` Sabrina Dubroca
2024-11-21 21:27     ` Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 07/23] ovpn: introduce the ovpn_socket object Antonio Quartulli
2024-11-10 18:26   ` Sergey Ryazanov
2024-11-15 14:28     ` Antonio Quartulli
2024-11-19 13:44       ` Antonio Quartulli
2024-11-20 23:34         ` Sergey Ryazanov
2024-11-21 21:29           ` Antonio Quartulli
2024-11-20 23:58       ` Sergey Ryazanov
2024-11-21 21:36         ` Antonio Quartulli
2024-11-22  8:08           ` Sergey Ryazanov
2024-10-29 10:47 ` [PATCH net-next v11 08/23] ovpn: implement basic TX path (UDP) Antonio Quartulli
2024-10-30 17:14   ` Sabrina Dubroca
2024-10-30 20:58     ` Antonio Quartulli
2024-11-10 22:32   ` Sergey Ryazanov
2024-11-12 17:28     ` Sabrina Dubroca
2024-11-14 15:25     ` Antonio Quartulli
2024-11-10 23:54   ` Sergey Ryazanov
2024-11-15 14:39     ` Antonio Quartulli
2024-11-21  0:29       ` Sergey Ryazanov
2024-11-21 21:39         ` Antonio Quartulli
2024-11-20 11:45   ` Sabrina Dubroca
2024-11-21 21:41     ` Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 09/23] ovpn: implement basic RX " Antonio Quartulli
2024-10-31 11:29   ` Sabrina Dubroca
2024-10-31 13:04     ` Antonio Quartulli
2024-11-11  1:54   ` Sergey Ryazanov
2024-11-15 15:02     ` Antonio Quartulli
2024-11-26  0:32       ` Sergey Ryazanov
2024-11-26  8:49         ` Antonio Quartulli
2024-11-27  1:40           ` Antonio Quartulli
2024-11-29 13:20             ` Sabrina Dubroca
2024-12-01 23:34               ` Antonio Quartulli
2024-11-29 16:10         ` Sabrina Dubroca
2024-12-01 23:39           ` Antonio Quartulli
2024-12-02  3:53           ` Antonio Quartulli
2024-11-12  0:16   ` Sergey Ryazanov
2024-11-15 15:05     ` Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 10/23] ovpn: implement packet processing Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 11/23] ovpn: store tunnel and transport statistics Antonio Quartulli
2024-10-31 11:37   ` Sabrina Dubroca
2024-10-31 13:12     ` Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 12/23] ovpn: implement TCP transport Antonio Quartulli
2024-10-31 14:30   ` Antonio Quartulli
2024-10-31 15:25   ` Sabrina Dubroca [this message]
2024-11-16  0:33     ` Antonio Quartulli
2024-11-26  1:05       ` Sergey Ryazanov
2024-11-26  8:51         ` Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 13/23] ovpn: implement multi-peer support Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 14/23] ovpn: implement peer lookup logic Antonio Quartulli
2024-11-04 11:26   ` Sabrina Dubroca
2024-11-12  1:18     ` Sergey Ryazanov
2024-11-12 12:32       ` Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 15/23] ovpn: implement keepalive mechanism Antonio Quartulli
2024-11-05 18:10   ` Sabrina Dubroca
2024-11-12 13:20     ` Antonio Quartulli
2024-11-13 10:36       ` Sabrina Dubroca
2024-11-14  8:12         ` Antonio Quartulli
2024-11-14  9:03           ` Sabrina Dubroca
2024-11-22  9:41       ` Antonio Quartulli
2024-11-22 16:18         ` Sabrina Dubroca
2024-11-24  0:28           ` Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 16/23] ovpn: add support for updating local UDP endpoint Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 17/23] ovpn: add support for peer floating Antonio Quartulli
2024-11-04 11:24   ` Sabrina Dubroca
2024-11-12 13:52     ` Antonio Quartulli
2024-11-12 10:56   ` Sabrina Dubroca
2024-11-12 14:03     ` Antonio Quartulli
2024-11-13 11:25       ` Sabrina Dubroca
2024-11-14  8:26         ` Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 18/23] ovpn: implement peer add/get/dump/delete via netlink Antonio Quartulli
2024-11-04 15:14   ` Sabrina Dubroca
2024-11-12 14:19     ` Antonio Quartulli
2024-11-13 16:56       ` Sabrina Dubroca
2024-11-14  9:21         ` Antonio Quartulli
2024-11-20 11:12           ` Sabrina Dubroca
2024-11-20 11:34             ` Antonio Quartulli
2024-11-20 12:10               ` Sabrina Dubroca
2024-11-11 15:41   ` Sabrina Dubroca
2024-11-12 14:26     ` Antonio Quartulli
2024-11-13 11:05       ` Sabrina Dubroca
2024-11-14 10:32         ` Antonio Quartulli
2024-11-29 17:00           ` Sabrina Dubroca
2024-12-01 23:43             ` Antonio Quartulli
2024-11-21 16:02   ` Sabrina Dubroca
2024-11-21 21:43     ` Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 19/23] ovpn: implement key add/get/del/swap " Antonio Quartulli
2024-11-05 10:16   ` Sabrina Dubroca
2024-11-12 15:40     ` Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 20/23] ovpn: kill key and notify userspace in case of IV exhaustion Antonio Quartulli
2024-11-05 10:33   ` Sabrina Dubroca
2024-11-12 15:44     ` Antonio Quartulli
2024-11-13 14:28       ` Sabrina Dubroca
2024-11-14 10:38         ` Antonio Quartulli
2024-11-20 12:17           ` Sabrina Dubroca
2024-10-29 10:47 ` [PATCH net-next v11 21/23] ovpn: notify userspace when a peer is deleted Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 22/23] ovpn: add basic ethtool support Antonio Quartulli
2024-10-29 10:47 ` [PATCH net-next v11 23/23] testing/selftests: add test tool and scripts for ovpn module Antonio Quartulli
2024-10-31 10:00 ` [PATCH net-next v11 00/23] Introducing OpenVPN Data Channel Offload Antonio Quartulli
2024-11-01  2:12   ` Jakub Kicinski
2024-11-01  2:20 ` patchwork-bot+netdevbpf
2024-11-06  1:18 ` Sergey Ryazanov
2024-11-14 15:33   ` Antonio Quartulli
2024-11-14 22:10     ` Sergey Ryazanov
2024-11-15 15:08       ` Antonio Quartulli

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ZyOhe3yKTiqCF2TH@hog \
    --to=sd@queasysnail.net \
    --cc=andrew@lunn.ch \
    --cc=antonio@openvpn.net \
    --cc=donald.hunter@gmail.com \
    --cc=edumazet@google.com \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=ryazanov.s.a@gmail.com \
    --cc=shuah@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).