From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=74.125.229.5 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794601339; b=zslZMlpmLQ4OKk9t8JubsyxLuz1s4QB9PLlefT+dqUPRIXMUawuax9Zf99oyXuwCF+7d AogCMKnH6LK+Rf73hFWwKigbAsnEqcr52EvA7qbHN8PNu6N6zVss8d8TCcl4crI74m52q Q6mGGW/CfpzvsUwaZDGzidCbdz/VwlMkf8h9VMNvXgBxRjRdpiMmisy2eiuDERiOpfy+/ d+JNc3v1aXDUks128VNhThpl2LkqWtSzUQdjEHoQA3dRDLiW1DuZhdXZzEmX/eZ8rYbb/ HQEP/5dkplQqFkpR/UpfnUcqGmKf8j06udldG3ufuP4YM06xm2Ea+XK1xDNrcHvMenA== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794601339; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Forwarded-Encrypted:X-Gm-Message-State:X-Gm-Gg:X-Received:Received: Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; bh=e9XTism2h6GI5PEf3nzwBx2axJe/PDQpM09YQnNfZH0=; b=2vNlI6PspOLaqrPe6A7C1m/TDtiFS7fdynOS1Z66VCoVBiLFtzWHqrrsr1oi98eIsrVs UWljXNywxT0wUymqS1xzuJcQmqjrsd58U4UOpj2hr76MocAAnp22tuNNwDTE13pX3xD5f nWaGv2Cf6bv/y5DOqgQZLmsIaHDVoPdl1zbsquwPNbRWeCXoOHbZ7CigUTJLBOoM5ysfz FyUhVNdIZV6+tloIYAJgQT0ZVeb82s/L+IDfPJFQ3jL6+fmGhai+M61wrizDG64Z9ua4h /F2Su9x+p9E5/Q1bf0QFO5Lop7RMkVlX22gNXU1I4AApU6Pu3weY+oE4eODtmBjqvpg== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=LpREJwCt; arc=none smtp.remote-ip=74.125.229.5 Received: from mail-dy2-f5.google.com (mail-dy2-f5.google.com [74.125.229.5]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 89AE4606A332 for ; Fri, 13 Nov 2026 20:22:18 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 89AE4606A332 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=LpREJwCt Received: by mail-dy2-f5.google.com with SMTP id 5a478bee46e88-36d73027aedso486945eec.1 for ; Fri, 13 Nov 2026 12:22:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1794601337; x=1795206137; darn=vger.kernel.org; h=in-reply-to:content-disposition:content-type:mime-version :references:message-id:subject:cc:to:from:date:from:to:cc:subject :date:message-id:reply-to:content-type; bh=e9XTism2h6GI5PEf3nzwBx2axJe/PDQpM09YQnNfZH0=; b=LpREJwCtrnK7Eiqzb4f/1THoEjElqDwm2lkFHnjYviKx/56ONT1jzBZVgpJSV/uqj3 MvLDZl/7u8i1w39T6tT4486Fem3k99YZ5BLy0pIirLAzYlPOnzjV3LdbRqIWkiQWEw5T 4JVT+c8vI9qL1ToLnzaXPEg9ZeAY7aR4BoVIPKURMRnKB6aqGWD57RxStkAJUpNJgIVU EISm+PfhbJrYHt+2bUY1RRBN/QL8U0Qxx096MCSAqUYCSPmB8MeUvVwTbdnPoEX0Rhiv 6vOBkawWL4rduchCEHDlTjzTEOEt5i4FLiPbEbuNLMZruMbCrhNtq6qeIIRLLFQ0heLT aBqA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1794601337; x=1795206137; h=in-reply-to:content-disposition:content-type:mime-version :references:message-id:subject:cc:to:from:date:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=e9XTism2h6GI5PEf3nzwBx2axJe/PDQpM09YQnNfZH0=; b=T92GCdCLQucIn1Eh54P3zGAL4bzFwDqoec5v+7MdRcSaMfpiNzWAiJY6dEsPX1WjmU RTBvmOphNsqZKBq/iE+cFWb+qZzYND5/f8N7gia7hqY716ZYCp0+hoxWsq12J7OiclP8 YlL6h3AT0AnnDxtKZcAnmDM11sn1DjVofuA/QVgIO3PvTugc739S7Kwv0hUGIoyS3UwF JlmrCWnZYae1k0BcCYYSpINfHoaYwR6jnZPpUVJseKLhtWXEPEaVogYVfsWiIAnilhJS 88yM+l0E8jZrDCHrxO4guAMixaUinSNy0naZ+fmj01ivnsIUVLVnunfi3km+RIgYWLiL ttgA== X-Forwarded-Encrypted: i=1; AL+hErHbp5Fe9PA/rT7I82ag6WCQ6XaqoyI+3xXbV3kmZo+TkZ8nknTOWjwl2ceA8H7ssA5T5C0IJgY=@vger.kernel.org X-Gm-Message-State: APuU4Z/xYQ3N1FAyrFNAO6auyR7tgFXwSOEImzD8OoGmu3oxMZZwJlV7 nL1taFQ95IoKSgHti8GAOFgnhZwbUbj7HVh6fnDBtEKb+9cALhoFVvMAKyqtaJz4/Yg= X-Gm-Gg: AYBFou1bunxjFDVjdUIUHIxsy4NRVKV64ijxDMgj6MEvIVK6Z+7fXoBfB3QI9zHmT8V DvgRP61Vml32l6zpBWaHo044+3Jt1/Y0AG35TX588l+gNSOIeWnuzFAYnw01UciKjfkEXnfUd3x 3Smk0lt7UFnpN35F71PHUEoiyTZBQcZdXipIyyc5xgtu5gcwIPaD+MN4d4Z9nb++lZtibOEe0fy ShD5wE0UC5u4OuNvRnpKmqLZtUs6UdmBN+NxJDgFp2TSjP/8dxMxR8nOJ8rZ+kLvsr5PJPi8iIH 2sQwwLGbRHxxw1EhH3aqTevDDo4xl3wx9TiPsaLQny/1ogkbU3IA2bmVsD/QxdfnXebPL0yDwRw 9+bFmXpE405E6L/4PwKI7A6r0VBv4sIr8FVIpiU6z502KJ+q+PA5Gm3kbmYdYLzDxPYz/A1LcMg uHcAHMklHN8vDQ9y6I+kubhnbi7j8l8JlFifeoGucv1FWW474D+UN1wC+usTrKA7oZl6uD3aY6+ 5JQpe0/nDMCwkPOUR3MlfudpbeiEX4zwdFdgYluTT2UDyZtHdw+mI0pYobL3+KcZ7kyvZHOf6bm NRQ1YHI144thE9HEcZ5+iFI= X-Received: by 2002:a05:693c:2585:b0:369:ef63:ea95 with SMTP id 5a478bee46e88-37966e3a7e4mr2520589eec.6.1794601337204; Fri, 13 Nov 2026 12:22:17 -0800 (PST) Received: from localhost ([2a03:2880:9ff:43::]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-3795591f4f8sm9979449eec.13.2026.11.13.12.22.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 13 Nov 2026 12:22:16 -0800 (PST) Date: Fri, 13 Nov 2026 12:21:27 -0800 From: Stanislav Fomichev To: Breno Leitao Cc: "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Kuniyuki Iwashima , Willem de Bruijn , Neal Cardwell , Daniel Borkmann , John Fastabend , Stanislav Fomichev , Martin KaFai Lau , Alexei Starovoitov , Andrii Nakryiko , Eduard Zingerman , Kumar Kartikeya Dwivedi , Song Liu , Yonghong Song , Jiri Olsa , Emil Tsalapatis , Ihor Solodrai , Shuah Khan , 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 Message-ID: References: <20261112-getsockopt_phase7-v1-0-1eb759dd0929@debian.org> <20261112-getsockopt_phase7-v1-1-1eb759dd0929@debian.org> X-Mailing-List: netdev@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline 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 > --- > 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?