mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Stanislav Fomichev <sdf.kernel@gmail.com>
To: Breno Leitao <leitao@debian.org>
Cc: "David S. Miller" <davem@davemloft.net>,
	 Eric Dumazet <edumazet@kernel.org>,
	Jakub Kicinski <kuba@kernel.org>,
	 Paolo Abeni <pabeni@redhat.com>, Simon Horman <horms@kernel.org>,
	 Kuniyuki Iwashima <kuniyu@google.com>,
	Willem de Bruijn <willemb@google.com>,
	 Neal Cardwell <ncardwell@google.com>,
	Daniel Borkmann <daniel@iogearbox.net>,
	 John Fastabend <john.fastabend@gmail.com>,
	Stanislav Fomichev <sdf@fomichev.me>,
	 Martin KaFai Lau <martin.lau@linux.dev>,
	Alexei Starovoitov <ast@kernel.org>,
	 Andrii Nakryiko <andrii@kernel.org>,
	Eduard Zingerman <eddyz87@gmail.com>,
	 Kumar Kartikeya Dwivedi <memxor@gmail.com>,
	Song Liu <song@kernel.org>,
	 Yonghong Song <yonghong.song@linux.dev>,
	Jiri Olsa <jolsa@kernel.org>,
	 Emil Tsalapatis <emil@etsalapatis.com>,
	Ihor Solodrai <ihor.solodrai@linux.dev>,
	 Shuah Khan <shuah@kernel.org>,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	 bpf@vger.kernel.org, linux-kselftest@vger.kernel.org,
	kernel-team@meta.com
Subject: Re: [PATCH net-next 1/3] net/tcp-ao: convert the getsockopt helpers to sockopt_t
Date: Fri, 13 Nov 2026 12:21:27 -0800	[thread overview]
Message-ID: <avdxDgmlDBTRgqM0@devvm16445.scu0.facebook.com> (raw)
In-Reply-To: <20261112-getsockopt_phase7-v1-1-1eb759dd0929@debian.org>

On 11/12, Breno Leitao wrote:
> TCP_AO_GET_KEYS, TCP_AO_INFO and TCP_AO_REPAIR are read through
> tcp_ao_get_mkts(), tcp_ao_get_sock_info() and tcp_ao_get_repair(),
> reached from do_tcp_getsockopt() and from nowhere else. Convert them to
> sockopt_t. While do_tcp_getsockopt() still carries a sockptr_t pair, it
> calls them through tcp_ao_get_sockptr(), which builds the sockopt_t and
> writes optlen back.
> 
> TCP_AO_GET_KEYS and TCP_AO_INFO read a versioned struct from optval.
> Only a user buffer gets there, as BPF's sol_tcp_sockopt() takes no
> TCP-AO option, so they keep reading it with copy_struct_from_user(),
> now on iter_in.ubuf. A kernel buffer gets -EINVAL, as in
> sockopt_expand_out().
> 
> TCP_AO_GET_KEYS is sized by a field in optval rather than by optlen:
> optlen is the size of one entry, and nkeys says how many entries the
> buffer holds. The match count then goes into the first entry's nkeys,
> past optlen when the caller's struct is shorter than that field.
> 
> Userspace relies on this, so sockopt_expand_out() grows optval for the
> reply, as for MCAST_MSFILTER. The growth is bounded by the keys on the
> socket, not by nkeys alone: sockopt_expand_out() refuses a reply above
> MAX_RW_COUNT, and a large nkeys would otherwise get -EINVAL.
> 
> iter_out only moves forward, so the entries are written in turn and the
> bytes past each one are skipped, as before. The match count is written
> last, through a copy of iter_out saved at the head of optval.
> 
> A reply that still needs more than MAX_RW_COUNT bytes now fails with
> -EINVAL. With two keys on the socket, that takes entries of about 1 GiB.
> 
> TCP_AO_INFO and TCP_AO_REPAIR now write optlen back on success, with
> the value they were given. TCP_AO_GET_KEYS stores optlen after nkeys
> instead of before it, which only shows when one of the two writes
> faults.
> 
> Signed-off-by: Breno Leitao <leitao@debian.org>
> ---
>  include/net/tcp_ao.h | 13 ++++-----
>  net/ipv4/tcp.c       | 28 +++++++++++++++++--
>  net/ipv4/tcp_ao.c    | 77 ++++++++++++++++++++++++++++++----------------------
>  3 files changed, 76 insertions(+), 42 deletions(-)
> 
> diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
> index 9a2333e62e9940..0e1edd777a600a 100644
> --- a/include/net/tcp_ao.h
> +++ b/include/net/tcp_ao.h
> @@ -210,9 +210,9 @@ void tcp_ao_calc_traffic_key(const struct tcp_ao_key *mkt, u8 *traffic_key,
>  void tcp_ao_destroy_sock(struct sock *sk, bool twsk);
>  void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp);
>  bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code);
> -int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen);
> -int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen);
> -int tcp_ao_get_repair(struct sock *sk, sockptr_t optval, sockptr_t optlen);
> +int tcp_ao_get_mkts(struct sock *sk, sockopt_t *sopt);
> +int tcp_ao_get_sock_info(struct sock *sk, sockopt_t *sopt);
> +int tcp_ao_get_repair(struct sock *sk, sockopt_t *sopt);
>  int tcp_ao_set_repair(struct sock *sk, sockptr_t optval, unsigned int optlen);
>  enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk,
>  			const struct sk_buff *skb, unsigned short int family,
> @@ -334,18 +334,17 @@ static inline void tcp_ao_connect_init(struct sock *sk)
>  {
>  }
>  
> -static inline int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen)
> +static inline int tcp_ao_get_mkts(struct sock *sk, sockopt_t *sopt)
>  {
>  	return -ENOPROTOOPT;
>  }
>  
> -static inline int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen)
> +static inline int tcp_ao_get_sock_info(struct sock *sk, sockopt_t *sopt)
>  {
>  	return -ENOPROTOOPT;
>  }
>  
> -static inline int tcp_ao_get_repair(struct sock *sk,
> -				    sockptr_t optval, sockptr_t optlen)
> +static inline int tcp_ao_get_repair(struct sock *sk, sockopt_t *sopt)
>  {
>  	return -ENOPROTOOPT;
>  }
> diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
> index a1f89c1fa2ae51..552e8fe22e7e20 100644
> --- a/net/ipv4/tcp.c
> +++ b/net/ipv4/tcp.c
> @@ -4492,6 +4492,25 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk,
>  	return stats;
>  }
>  
> +/* Bridge the sockptr_t pair of do_tcp_getsockopt() to a TCP-AO getter. */
> +static int tcp_ao_get_sockptr(struct sock *sk,
> +			      int (*get)(struct sock *sk, sockopt_t *sopt),
> +			      sockptr_t optval, sockptr_t optlen)
> +{
> +	struct kvec kvec;
> +	sockopt_t sopt;
> +	int err;
> +
> +	err = sockptr_to_sockopt(&sopt, optval, optlen, &kvec);
> +	if (err)
> +		return err;
> +
> +	err = get(sk, &sopt);
> +	if (!err && copy_to_sockptr(optlen, &sopt.optlen, sizeof(int)))
> +		err = -EFAULT;
> +	return err;
> +}
> +
>  int do_tcp_getsockopt(struct sock *sk, int level,
>  		      int optname, sockptr_t optval, sockptr_t optlen)
>  {
> @@ -4830,16 +4849,19 @@ int do_tcp_getsockopt(struct sock *sk, int level,
>  	case TCP_AO_REPAIR:
>  		if (!tcp_can_repair_sock(sk))
>  			return -EPERM;
> -		return tcp_ao_get_repair(sk, optval, optlen);
> +		return tcp_ao_get_sockptr(sk, tcp_ao_get_repair,
> +					  optval, optlen);
>  	case TCP_AO_GET_KEYS:
>  	case TCP_AO_INFO: {
>  		int err;
>  
>  		sockopt_lock_sock(sk);
>  		if (optname == TCP_AO_GET_KEYS)
> -			err = tcp_ao_get_mkts(sk, optval, optlen);
> +			err = tcp_ao_get_sockptr(sk, tcp_ao_get_mkts,
> +						 optval, optlen);
>  		else
> -			err = tcp_ao_get_sock_info(sk, optval, optlen);
> +			err = tcp_ao_get_sockptr(sk, tcp_ao_get_sock_info,
> +						 optval, optlen);
>  		sockopt_release_sock(sk);
>  
>  		return err;
> diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
> index bb7bbc20ba3fad..af33042478f9a8 100644
> --- a/net/ipv4/tcp_ao.c
> +++ b/net/ipv4/tcp_ao.c
> @@ -2054,14 +2054,11 @@ int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen)
>  	return tcp_parse_ao(sk, cmd, AF_INET, optval, optlen);
>  }
>  
> -/* tcp_ao_copy_mkts_to_user(ao_info, optval, optlen)
> +/* tcp_ao_copy_mkts_to_user(ao_info, sopt)
>   *
>   * @ao_info:	struct tcp_ao_info on the socket that
>   *		socket getsockopt(TCP_AO_GET_KEYS) is executed on
> - * @optval:	pointer to array of tcp_ao_getsockopt structures in user space.
> - *		Must be != NULL.
> - * @optlen:	pointer to size of tcp_ao_getsockopt structure.
> - *		Must be != NULL.
> + * @sopt:	optval and optlen of that getsockopt(), described below.
>   *
>   * Return value: 0 on success, a negative error number otherwise.
>   *
> @@ -2091,29 +2088,31 @@ int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen)
>   */
>  static int tcp_ao_copy_mkts_to_user(const struct sock *sk,
>  				    struct tcp_ao_info *ao_info,
> -				    sockptr_t optval, sockptr_t optlen)
> +				    sockopt_t *sopt)
>  {
>  	struct tcp_ao_getsockopt opt_in, opt_out;
>  	struct tcp_ao_key *key, *current_key;
>  	bool do_address_matching = true;
>  	union tcp_ao_addr *addr = NULL;
>  	int err, l3index, user_len;
> +	unsigned int nr_keys = 0;
>  	unsigned int max_keys;	/* maximum number of keys to copy to user */
> -	size_t out_offset = 0;
>  	size_t bytes_to_write;	/* number of bytes to write to user level */
> +	struct iov_iter head;
>  	u32 matched_keys;	/* keys from ao_info matched so far */
> -	int optlen_out;
> +	size_t reply_len;
>  	__be16 port = 0;
>  
> -	if (copy_from_sockptr(&user_len, optlen, sizeof(int)))
> -		return -EFAULT;
> +	user_len = sopt->optlen;
>  
>  	if (user_len <= 0)
>  		return -EINVAL;
>  
>  	memset(&opt_in, 0, sizeof(struct tcp_ao_getsockopt));
> -	err = copy_struct_from_sockptr(&opt_in, sizeof(opt_in),
> -				       optval, user_len);
> +	if (WARN_ON_ONCE(!iter_is_ubuf(&sopt->iter_in)))
> +		return -EINVAL;
> +	err = copy_struct_from_user(&opt_in, sizeof(opt_in), sopt->iter_in.ubuf,
> +				    user_len);
>  	if (err < 0)
>  		return err;
>  
> @@ -2214,6 +2213,21 @@ static int tcp_ao_copy_mkts_to_user(const struct sock *sk,
>  	}
>  
>  	bytes_to_write = min_t(int, user_len, sizeof(struct tcp_ao_getsockopt));
> +

[..]

> +	/* The reply is up to nkeys entries, user_len apart, with the match
> +	 * count in the first entry's nkeys, so it can outgrow optlen. Size it
> +	 * by the keys on the socket rather than by the caller's nkeys.
> +	 */
> +	hlist_for_each_entry_rcu(key, &ao_info->head, node,
> +				 lockdep_sock_is_held(sk))
> +		nr_keys++;
> +	reply_len = max(array_size(min(max_keys, nr_keys), user_len),
> +			offsetofend(struct tcp_ao_getsockopt, nkeys));
> +	err = sockopt_expand_out(sopt, reply_len);
> +	if (err)
> +		return err;
> +	head = sopt->iter_out;

Is that faster / cleaner / more preferable than calling sockopt_expand_out
on every iteration hlist_for_each_entry_rcu below?

  reply	other threads:[~2026-11-13 20:22 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-12 17:05 [PATCH net-next 0/3] tcp: convert the SOL_TCP getsockopt path " Breno Leitao
2026-11-12 17:05 ` [PATCH net-next 1/3] net/tcp-ao: convert the getsockopt helpers " Breno Leitao
2026-11-13 20:21   ` Stanislav Fomichev [this message]
2026-11-12 17:05 ` [PATCH net-next 2/3] tcp: convert do_tcp_getsockopt() " Breno Leitao
2026-11-13 20:21   ` Stanislav Fomichev
2026-11-12 17:05 ` [PATCH net-next 3/3] selftests: net: getsockopt_iter: cover tcp Breno Leitao
2026-11-13 20:22   ` Stanislav Fomichev

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=avdxDgmlDBTRgqM0@devvm16445.scu0.facebook.com \
    --to=sdf.kernel@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=davem@davemloft.net \
    --cc=eddyz87@gmail.com \
    --cc=edumazet@kernel.org \
    --cc=emil@etsalapatis.com \
    --cc=horms@kernel.org \
    --cc=ihor.solodrai@linux.dev \
    --cc=john.fastabend@gmail.com \
    --cc=jolsa@kernel.org \
    --cc=kernel-team@meta.com \
    --cc=kuba@kernel.org \
    --cc=kuniyu@google.com \
    --cc=leitao@debian.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=martin.lau@linux.dev \
    --cc=memxor@gmail.com \
    --cc=ncardwell@google.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=sdf@fomichev.me \
    --cc=shuah@kernel.org \
    --cc=song@kernel.org \
    --cc=willemb@google.com \
    --cc=yonghong.song@linux.dev \
    /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

all inboxes | Powered by JetHome®