diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c index 3912e75079f5..b273213cc68d 100644 --- a/net/kcm/kcmsock.c +++ b/net/kcm/kcmsock.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -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, diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 1b210db3119e..35278c519a30 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -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, diff --git a/net/mctp/af_mctp.c b/net/mctp/af_mctp.c index 209a963112e3..8af5e2b3c8d1 100644 --- a/net/mctp/af_mctp.c +++ b/net/mctp/af_mctp.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -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, diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index af8762b24039..c31d2af5dd22 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #include @@ -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, diff --git a/tools/testing/selftests/net/getsockopt_iter.c b/tools/testing/selftests/net/getsockopt_iter.c index 179f9e84926f..209569354d0e 100644 --- a/tools/testing/selftests/net/getsockopt_iter.c +++ b/tools/testing/selftests/net/getsockopt_iter.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #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