Merge branch 'net-convert-four-more-protocols-to-getsockopt_iter'

Breno Leitao says:

====================
net: convert four more protocols to getsockopt_iter

Continue the work to convert protocols to the new getsockopt_iter API.

Convert four additional getsockopt implementations to the new
sockopt_t/getsockopt_iter callback:

  - MCTP
  - LLC
  - X.25
  - KCM

These are mechanical, ABI-preserving conversions following the same
pattern as the previously converted protocols (af_packet, can/raw,
af_netlink, af_vsock): the (char __user *optval, int __user *optlen)
pair is replaced with a single sockopt_t *opt that carries the buffer
length on input and the returned size on output, and exposes an iov_iter
for the copy-out path. put_user()/copy_to_user() pairs are replaced with
a single copy_to_iter() per option, and the wrapper in
do_sock_getsockopt() handles writing optlen back to userspace.

I picked these four because each is small and self-contained.
====================

Link: https://patch.msgid.link/20260507-getsock_two-v2-0-5873111d9c12@debian.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-05-10 10:11:10 -07:00
commit 7247c92cdc
5 changed files with 124 additions and 45 deletions

View File

@ -24,6 +24,7 @@
#include <linux/workqueue.h>
#include <linux/syscalls.h>
#include <linux/sched/signal.h>
#include <linux/uio.h>
#include <net/kcm.h>
#include <net/netns/generic.h>
@ -1167,7 +1168,7 @@ static int kcm_setsockopt(struct socket *sock, int level, int optname,
}
static int kcm_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
sockopt_t *opt)
{
struct kcm_sock *kcm = kcm_sk(sock->sk);
int val, len;
@ -1175,9 +1176,7 @@ static int kcm_getsockopt(struct socket *sock, int level, int optname,
if (level != SOL_KCM)
return -ENOPROTOOPT;
if (get_user(len, optlen))
return -EFAULT;
len = opt->optlen;
if (len < 0)
return -EINVAL;
@ -1191,9 +1190,8 @@ static int kcm_getsockopt(struct socket *sock, int level, int optname,
return -ENOPROTOOPT;
}
if (put_user(len, optlen))
return -EFAULT;
if (copy_to_user(optval, &val, len))
opt->optlen = len;
if (copy_to_iter(&val, len, &opt->iter_out) != len)
return -EFAULT;
return 0;
}
@ -1755,7 +1753,7 @@ static const struct proto_ops kcm_dgram_ops = {
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = kcm_setsockopt,
.getsockopt = kcm_getsockopt,
.getsockopt_iter = kcm_getsockopt,
.sendmsg = kcm_sendmsg,
.recvmsg = kcm_recvmsg,
.mmap = sock_no_mmap,
@ -1776,7 +1774,7 @@ static const struct proto_ops kcm_seqpacket_ops = {
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = kcm_setsockopt,
.getsockopt = kcm_getsockopt,
.getsockopt_iter = kcm_getsockopt,
.sendmsg = kcm_sendmsg,
.recvmsg = kcm_recvmsg,
.mmap = sock_no_mmap,

View File

@ -27,6 +27,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/sched/signal.h>
#include <linux/uio.h>
#include <net/llc.h>
#include <net/llc_sap.h>
@ -1166,25 +1167,21 @@ static int llc_ui_setsockopt(struct socket *sock, int level, int optname,
* @sock: Socket to get information from.
* @level: Socket level user is requesting operations on.
* @optname: Operation name.
* @optval: Variable to return operation data in.
* @optlen: Length of optval.
* @opt: sockopt context with iterator and length for returning data.
*
* Get connection specific socket information.
*/
static int llc_ui_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
sockopt_t *opt)
{
struct sock *sk = sock->sk;
struct llc_sock *llc = llc_sk(sk);
int val = 0, len = 0, rc = -EINVAL;
int val = 0, len, rc = -EINVAL;
lock_sock(sk);
if (unlikely(level != SOL_LLC))
goto out;
rc = get_user(len, optlen);
if (rc)
goto out;
rc = -EINVAL;
len = opt->optlen;
if (len != sizeof(int))
goto out;
switch (optname) {
@ -1212,7 +1209,8 @@ static int llc_ui_getsockopt(struct socket *sock, int level, int optname,
goto out;
}
rc = 0;
if (put_user(len, optlen) || copy_to_user(optval, &val, len))
opt->optlen = len;
if (copy_to_iter(&val, len, &opt->iter_out) != len)
rc = -EFAULT;
out:
release_sock(sk);
@ -1239,7 +1237,7 @@ static const struct proto_ops llc_ui_ops = {
.listen = llc_ui_listen,
.shutdown = llc_ui_shutdown,
.setsockopt = llc_ui_setsockopt,
.getsockopt = llc_ui_getsockopt,
.getsockopt_iter = llc_ui_getsockopt,
.sendmsg = llc_ui_sendmsg,
.recvmsg = llc_ui_recvmsg,
.mmap = sock_no_mmap,

View File

@ -12,6 +12,7 @@
#include <linux/mctp.h>
#include <linux/module.h>
#include <linux/socket.h>
#include <linux/uio.h>
#include <net/mctp.h>
#include <net/mctpdevice.h>
@ -405,7 +406,7 @@ static int mctp_setsockopt(struct socket *sock, int level, int optname,
}
static int mctp_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
sockopt_t *opt)
{
struct mctp_sock *msk = container_of(sock->sk, struct mctp_sock, sk);
int len, val;
@ -413,14 +414,13 @@ static int mctp_getsockopt(struct socket *sock, int level, int optname,
if (level != SOL_MCTP)
return -EINVAL;
if (get_user(len, optlen))
return -EFAULT;
len = opt->optlen;
if (optname == MCTP_OPT_ADDR_EXT) {
if (len != sizeof(int))
return -EINVAL;
val = !!msk->addr_ext;
if (copy_to_user(optval, &val, len))
if (copy_to_iter(&val, len, &opt->iter_out) != len)
return -EFAULT;
return 0;
}
@ -639,7 +639,7 @@ static const struct proto_ops mctp_dgram_ops = {
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = mctp_setsockopt,
.getsockopt = mctp_getsockopt,
.getsockopt_iter = mctp_getsockopt,
.sendmsg = mctp_sendmsg,
.recvmsg = mctp_recvmsg,
.mmap = sock_no_mmap,

View File

@ -53,6 +53,7 @@
#include <linux/init.h>
#include <linux/compat.h>
#include <linux/ctype.h>
#include <linux/uio.h>
#include <net/x25.h>
#include <net/compat.h>
@ -448,7 +449,7 @@ static int x25_setsockopt(struct socket *sock, int level, int optname,
}
static int x25_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
sockopt_t *opt)
{
struct sock *sk = sock->sk;
int val, len, rc = -ENOPROTOOPT;
@ -456,22 +457,17 @@ static int x25_getsockopt(struct socket *sock, int level, int optname,
if (level != SOL_X25 || optname != X25_QBITINCL)
goto out;
rc = -EFAULT;
if (get_user(len, optlen))
goto out;
len = opt->optlen;
rc = -EINVAL;
if (len < 0)
goto out;
len = min_t(unsigned int, len, sizeof(int));
rc = -EFAULT;
if (put_user(len, optlen))
goto out;
opt->optlen = len;
val = test_bit(X25_Q_BIT_FLAG, &x25_sk(sk)->flags);
rc = copy_to_user(optval, &val, len) ? -EFAULT : 0;
rc = copy_to_iter(&val, len, &opt->iter_out) != len ? -EFAULT : 0;
out:
return rc;
}
@ -1753,7 +1749,7 @@ static const struct proto_ops x25_proto_ops = {
.listen = x25_listen,
.shutdown = sock_no_shutdown,
.setsockopt = x25_setsockopt,
.getsockopt = x25_getsockopt,
.getsockopt_iter = x25_getsockopt,
.sendmsg = x25_sendmsg,
.recvmsg = x25_recvmsg,
.mmap = sock_no_mmap,

View File

@ -22,6 +22,7 @@
#include <unistd.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/time_types.h>
#include <linux/vm_sockets.h>
#include <sys/socket.h>
#include "kselftest_harness.h"
@ -61,8 +62,10 @@ FIXTURE_TEARDOWN(netlink)
TEST_F(netlink, pktinfo_exact)
{
socklen_t optlen;
int val = -1;
socklen_t optlen = sizeof(val);
optlen = sizeof(val);
ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO,
&val, &optlen));
@ -73,7 +76,9 @@ TEST_F(netlink, pktinfo_exact)
TEST_F(netlink, pktinfo_oversize_clamped)
{
char buf[16] = {};
socklen_t optlen = sizeof(buf);
socklen_t optlen;
optlen = sizeof(buf);
ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO,
buf, &optlen));
@ -83,11 +88,14 @@ TEST_F(netlink, pktinfo_oversize_clamped)
TEST_F(netlink, pktinfo_undersize)
{
char buf[2] = {};
socklen_t optlen = sizeof(buf);
socklen_t optlen;
optlen = sizeof(buf);
ASSERT_EQ(-1, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO,
buf, &optlen));
ASSERT_EQ(EINVAL, errno);
ASSERT_EQ(sizeof(buf), optlen);
}
TEST_F(netlink, list_memberships_size_discovery)
@ -105,7 +113,9 @@ TEST_F(netlink, list_memberships_size_discovery)
TEST_F(netlink, list_memberships_full_read)
{
__u32 buf[64] = {};
socklen_t optlen = sizeof(buf);
socklen_t optlen;
optlen = sizeof(buf);
ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK,
NETLINK_LIST_MEMBERSHIPS,
@ -117,22 +127,28 @@ TEST_F(netlink, list_memberships_full_read)
TEST_F(netlink, bad_level)
{
socklen_t optlen;
int val;
socklen_t optlen = sizeof(val);
optlen = sizeof(val);
ASSERT_EQ(-1, getsockopt(self->fd, SOL_SOCKET + 1, NETLINK_PKTINFO,
&val, &optlen));
ASSERT_EQ(ENOPROTOOPT, errno);
ASSERT_EQ(sizeof(val), optlen);
}
TEST_F(netlink, bad_optname)
{
socklen_t optlen;
int val;
socklen_t optlen = sizeof(val);
optlen = sizeof(val);
ASSERT_EQ(-1, getsockopt(self->fd, SOL_NETLINK, 0x7fff,
&val, &optlen));
ASSERT_EQ(ENOPROTOOPT, errno);
ASSERT_EQ(sizeof(val), optlen);
}
/* ---------- vsock ---------- */
@ -157,8 +173,10 @@ FIXTURE_TEARDOWN(vsock)
TEST_F(vsock, buffer_size_exact)
{
socklen_t optlen;
uint64_t val = 0;
socklen_t optlen = sizeof(val);
optlen = sizeof(val);
ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK,
SO_VM_SOCKETS_BUFFER_SIZE,
@ -170,7 +188,9 @@ TEST_F(vsock, buffer_size_exact)
TEST_F(vsock, buffer_size_oversize_clamped)
{
char buf[16] = {};
socklen_t optlen = sizeof(buf);
socklen_t optlen;
optlen = sizeof(buf);
ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK,
SO_VM_SOCKETS_BUFFER_SIZE,
@ -181,33 +201,100 @@ TEST_F(vsock, buffer_size_oversize_clamped)
TEST_F(vsock, buffer_size_undersize)
{
char buf[4] = {};
socklen_t optlen = sizeof(buf);
socklen_t optlen;
optlen = sizeof(buf);
ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK,
SO_VM_SOCKETS_BUFFER_SIZE,
buf, &optlen));
ASSERT_EQ(EINVAL, errno);
ASSERT_EQ(sizeof(buf), optlen);
}
TEST_F(vsock, bad_level)
{
socklen_t optlen;
uint64_t val;
socklen_t optlen = sizeof(val);
optlen = sizeof(val);
ASSERT_EQ(-1, getsockopt(self->fd, SOL_SOCKET + 1,
SO_VM_SOCKETS_BUFFER_SIZE,
&val, &optlen));
ASSERT_EQ(ENOPROTOOPT, errno);
ASSERT_EQ(sizeof(val), optlen);
}
TEST_F(vsock, bad_optname)
{
socklen_t optlen;
uint64_t val;
socklen_t optlen = sizeof(val);
optlen = sizeof(val);
ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK, 0x7fff,
&val, &optlen));
ASSERT_EQ(ENOPROTOOPT, errno);
ASSERT_EQ(sizeof(val), optlen);
}
/* SO_VM_SOCKETS_CONNECT_TIMEOUT_{NEW,OLD} return a sock_timeval-shaped
* payload, which is wider than u64 on 64-bit. They exercise the path
* where the protocol's reported lv (16 bytes) is larger than the
* common 8-byte u64 case covered above.
*/
TEST_F(vsock, connect_timeout_new_exact)
{
struct __kernel_sock_timeval tv = {};
socklen_t optlen;
optlen = sizeof(tv);
ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK,
SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW,
&tv, &optlen));
ASSERT_EQ(sizeof(tv), optlen);
}
TEST_F(vsock, connect_timeout_new_oversize_clamped)
{
char buf[sizeof(struct __kernel_sock_timeval) * 2] = {};
socklen_t optlen;
optlen = sizeof(buf);
ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK,
SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW,
buf, &optlen));
ASSERT_EQ(sizeof(struct __kernel_sock_timeval), optlen);
}
TEST_F(vsock, connect_timeout_new_undersize)
{
socklen_t optlen;
uint64_t val;
optlen = sizeof(val);
ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK,
SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW,
&val, &optlen));
ASSERT_EQ(EINVAL, errno);
ASSERT_EQ(sizeof(val), optlen);
}
TEST_F(vsock, connect_timeout_old_exact)
{
struct __kernel_old_timeval tv = {};
socklen_t optlen;
optlen = sizeof(tv);
ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK,
SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD,
&tv, &optlen));
ASSERT_EQ(sizeof(tv), optlen);
}
TEST_HARNESS_MAIN