+++ /dev/null
-From 411791fe82c0b08a8c4ee827713133f47c2e890d Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Fri, 9 Jun 2023 08:27:42 -0700
-Subject: net: ioctl: Use kernel memory on protocol ioctl callbacks
-
-From: Breno Leitao <leitao@debian.org>
-
-[ Upstream commit e1d001fa5b477c4da46a29be1fcece91db7c7c6f ]
-
-Most of the ioctls to net protocols operates directly on userspace
-argument (arg). Usually doing get_user()/put_user() directly in the
-ioctl callback. This is not flexible, because it is hard to reuse these
-functions without passing userspace buffers.
-
-Change the "struct proto" ioctls to avoid touching userspace memory and
-operate on kernel buffers, i.e., all protocol's ioctl callbacks is
-adapted to operate on a kernel memory other than on userspace (so, no
-more {put,get}_user() and friends being called in the ioctl callback).
-
-This changes the "struct proto" ioctl format in the following way:
-
- int (*ioctl)(struct sock *sk, int cmd,
-- unsigned long arg);
-+ int *karg);
-
-(Important to say that this patch does not touch the "struct proto_ops"
-protocols)
-
-So, the "karg" argument, which is passed to the ioctl callback, is a
-pointer allocated to kernel space memory (inside a function wrapper).
-This buffer (karg) may contain input argument (copied from userspace in
-a prep function) and it might return a value/buffer, which is copied
-back to userspace if necessary. There is not one-size-fits-all format
-(that is I am using 'may' above), but basically, there are three type of
-ioctls:
-
-1) Do not read from userspace, returns a result to userspace
-2) Read an input parameter from userspace, and does not return anything
- to userspace
-3) Read an input from userspace, and return a buffer to userspace.
-
-The default case (1) (where no input parameter is given, and an "int" is
-returned to userspace) encompasses more than 90% of the cases, but there
-are two other exceptions. Here is a list of exceptions:
-
-* Protocol RAW:
- * cmd = SIOCGETVIFCNT:
- * input and output = struct sioc_vif_req
- * cmd = SIOCGETSGCNT
- * input and output = struct sioc_sg_req
- * Explanation: for the SIOCGETVIFCNT case, userspace passes the input
- argument, which is struct sioc_vif_req. Then the callback populates
- the struct, which is copied back to userspace.
-
-* Protocol RAW6:
- * cmd = SIOCGETMIFCNT_IN6
- * input and output = struct sioc_mif_req6
- * cmd = SIOCGETSGCNT_IN6
- * input and output = struct sioc_sg_req6
-
-* Protocol PHONET:
- * cmd == SIOCPNADDRESOURCE | SIOCPNDELRESOURCE
- * input int (4 bytes)
- * Nothing is copied back to userspace.
-
-For the exception cases, functions sock_sk_ioctl_inout() will
-copy the userspace input, and copy it back to kernel space.
-
-The wrapper that prepare the buffer and put the buffer back to user is
-sk_ioctl(), so, instead of calling sk->sk_prot->ioctl(), the callee now
-calls sk_ioctl(), which will handle all cases.
-
-Signed-off-by: Breno Leitao <leitao@debian.org>
-Reviewed-by: Willem de Bruijn <willemb@google.com>
-Reviewed-by: David Ahern <dsahern@kernel.org>
-Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
-Link: https://lore.kernel.org/r/20230609152800.830401-1-leitao@debian.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Stable-dep-of: 3b2d9bc4d4ac ("phonet: take correct lock to peek at the RX queue")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- include/linux/icmpv6.h | 6 ++++
- include/linux/mroute.h | 22 +++++++++++--
- include/linux/mroute6.h | 31 ++++++++++++++++--
- include/net/phonet/phonet.h | 21 ++++++++++++
- include/net/sock.h | 5 ++-
- include/net/tcp.h | 2 +-
- include/net/udp.h | 2 +-
- net/core/sock.c | 64 +++++++++++++++++++++++++++++++++++++
- net/dccp/dccp.h | 2 +-
- net/dccp/proto.c | 12 +++----
- net/ieee802154/socket.c | 15 ++++-----
- net/ipv4/af_inet.c | 2 +-
- net/ipv4/ipmr.c | 63 ++++++++++++++++++++++--------------
- net/ipv4/raw.c | 16 +++++-----
- net/ipv4/tcp.c | 5 +--
- net/ipv4/udp.c | 12 +++----
- net/ipv6/af_inet6.c | 2 +-
- net/ipv6/ip6mr.c | 44 +++++++++++--------------
- net/ipv6/raw.c | 16 +++++-----
- net/l2tp/l2tp_core.h | 2 +-
- net/l2tp/l2tp_ip.c | 9 +++---
- net/mptcp/protocol.c | 11 +++----
- net/phonet/datagram.c | 11 +++----
- net/phonet/pep.c | 11 +++----
- net/phonet/socket.c | 2 +-
- net/sctp/socket.c | 8 ++---
- 26 files changed, 267 insertions(+), 129 deletions(-)
-
-diff --git a/include/linux/icmpv6.h b/include/linux/icmpv6.h
-index db0f4fcfdaf4f..1fe33e6741cca 100644
---- a/include/linux/icmpv6.h
-+++ b/include/linux/icmpv6.h
-@@ -111,4 +111,10 @@ static inline bool icmpv6_is_err(int type)
- return false;
- }
-
-+static inline int sk_is_icmpv6(struct sock *sk)
-+{
-+ return sk->sk_family == AF_INET6 &&
-+ inet_sk(sk)->inet_num == IPPROTO_ICMPV6;
-+}
-+
- #endif
-diff --git a/include/linux/mroute.h b/include/linux/mroute.h
-index 80b8400ab8b24..94c6e6f549f0a 100644
---- a/include/linux/mroute.h
-+++ b/include/linux/mroute.h
-@@ -16,12 +16,19 @@ static inline int ip_mroute_opt(int opt)
- return opt >= MRT_BASE && opt <= MRT_MAX;
- }
-
-+static inline int sk_is_ipmr(struct sock *sk)
-+{
-+ return sk->sk_family == AF_INET &&
-+ inet_sk(sk)->inet_num == IPPROTO_IGMP;
-+}
-+
- int ip_mroute_setsockopt(struct sock *, int, sockptr_t, unsigned int);
- int ip_mroute_getsockopt(struct sock *, int, sockptr_t, sockptr_t);
--int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg);
-+int ipmr_ioctl(struct sock *sk, int cmd, void *arg);
- int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg);
- int ip_mr_init(void);
- bool ipmr_rule_default(const struct fib_rule *rule);
-+int ipmr_sk_ioctl(struct sock *sk, unsigned int cmd, void __user *arg);
- #else
- static inline int ip_mroute_setsockopt(struct sock *sock, int optname,
- sockptr_t optval, unsigned int optlen)
-@@ -35,7 +42,7 @@ static inline int ip_mroute_getsockopt(struct sock *sk, int optname,
- return -ENOPROTOOPT;
- }
-
--static inline int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
-+static inline int ipmr_ioctl(struct sock *sk, int cmd, void *arg)
- {
- return -ENOIOCTLCMD;
- }
-@@ -50,10 +57,21 @@ static inline int ip_mroute_opt(int opt)
- return 0;
- }
-
-+static inline int sk_is_ipmr(struct sock *sk)
-+{
-+ return 0;
-+}
-+
- static inline bool ipmr_rule_default(const struct fib_rule *rule)
- {
- return true;
- }
-+
-+static inline int ipmr_sk_ioctl(struct sock *sk, unsigned int cmd,
-+ void __user *arg)
-+{
-+ return 1;
-+}
- #endif
-
- #define VIFF_STATIC 0x8000
-diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h
-index 8f2b307fb1241..2f95d5b4e47af 100644
---- a/include/linux/mroute6.h
-+++ b/include/linux/mroute6.h
-@@ -29,10 +29,10 @@ struct sock;
- extern int ip6_mroute_setsockopt(struct sock *, int, sockptr_t, unsigned int);
- extern int ip6_mroute_getsockopt(struct sock *, int, sockptr_t, sockptr_t);
- extern int ip6_mr_input(struct sk_buff *skb);
--extern int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg);
- extern int ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg);
- extern int ip6_mr_init(void);
- extern void ip6_mr_cleanup(void);
-+int ip6mr_ioctl(struct sock *sk, int cmd, void *arg);
- #else
- static inline int ip6_mroute_setsockopt(struct sock *sock, int optname,
- sockptr_t optval, unsigned int optlen)
-@@ -48,7 +48,7 @@ int ip6_mroute_getsockopt(struct sock *sock,
- }
-
- static inline
--int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg)
-+int ip6mr_ioctl(struct sock *sk, int cmd, void *arg)
- {
- return -ENOIOCTLCMD;
- }
-@@ -100,6 +100,27 @@ extern int ip6mr_get_route(struct net *net, struct sk_buff *skb,
- #ifdef CONFIG_IPV6_MROUTE
- bool mroute6_is_socket(struct net *net, struct sk_buff *skb);
- extern int ip6mr_sk_done(struct sock *sk);
-+static inline int ip6mr_sk_ioctl(struct sock *sk, unsigned int cmd,
-+ void __user *arg)
-+{
-+ switch (cmd) {
-+ /* These userspace buffers will be consumed by ip6mr_ioctl() */
-+ case SIOCGETMIFCNT_IN6: {
-+ struct sioc_mif_req6 buffer;
-+
-+ return sock_ioctl_inout(sk, cmd, arg, &buffer,
-+ sizeof(buffer));
-+ }
-+ case SIOCGETSGCNT_IN6: {
-+ struct sioc_mif_req6 buffer;
-+
-+ return sock_ioctl_inout(sk, cmd, arg, &buffer,
-+ sizeof(buffer));
-+ }
-+ }
-+
-+ return 1;
-+}
- #else
- static inline bool mroute6_is_socket(struct net *net, struct sk_buff *skb)
- {
-@@ -109,5 +130,11 @@ static inline int ip6mr_sk_done(struct sock *sk)
- {
- return 0;
- }
-+
-+static inline int ip6mr_sk_ioctl(struct sock *sk, unsigned int cmd,
-+ void __user *arg)
-+{
-+ return 1;
-+}
- #endif
- #endif
-diff --git a/include/net/phonet/phonet.h b/include/net/phonet/phonet.h
-index 862f1719b5230..cf5ecae4a2fcd 100644
---- a/include/net/phonet/phonet.h
-+++ b/include/net/phonet/phonet.h
-@@ -109,4 +109,25 @@ void phonet_sysctl_exit(void);
- int isi_register(void);
- void isi_unregister(void);
-
-+static inline bool sk_is_phonet(struct sock *sk)
-+{
-+ return sk->sk_family == PF_PHONET;
-+}
-+
-+static inline int phonet_sk_ioctl(struct sock *sk, unsigned int cmd,
-+ void __user *arg)
-+{
-+ int karg;
-+
-+ switch (cmd) {
-+ case SIOCPNADDRESOURCE:
-+ case SIOCPNDELRESOURCE:
-+ if (get_user(karg, (int __user *)arg))
-+ return -EFAULT;
-+
-+ return sk->sk_prot->ioctl(sk, cmd, &karg);
-+ }
-+ /* A positive return value means that the ioctl was not processed */
-+ return 1;
-+}
- #endif
-diff --git a/include/net/sock.h b/include/net/sock.h
-index 579732d47dfc4..6ea961ee257d2 100644
---- a/include/net/sock.h
-+++ b/include/net/sock.h
-@@ -1258,7 +1258,7 @@ struct proto {
- bool kern);
-
- int (*ioctl)(struct sock *sk, int cmd,
-- unsigned long arg);
-+ int *karg);
- int (*init)(struct sock *sk);
- void (*destroy)(struct sock *sk);
- void (*shutdown)(struct sock *sk, int how);
-@@ -3048,6 +3048,9 @@ int sock_get_timeout(long timeo, void *optval, bool old_timeval);
- int sock_copy_user_timeval(struct __kernel_sock_timeval *tv,
- sockptr_t optval, int optlen, bool old_timeval);
-
-+int sock_ioctl_inout(struct sock *sk, unsigned int cmd,
-+ void __user *arg, void *karg, size_t size);
-+int sk_ioctl(struct sock *sk, unsigned int cmd, void __user *arg);
- static inline bool sk_is_readable(struct sock *sk)
- {
- if (sk->sk_prot->sock_is_readable)
-diff --git a/include/net/tcp.h b/include/net/tcp.h
-index 8ea1fba84eff9..8d3777e476e0e 100644
---- a/include/net/tcp.h
-+++ b/include/net/tcp.h
-@@ -346,7 +346,7 @@ void tcp_release_cb(struct sock *sk);
- void tcp_wfree(struct sk_buff *skb);
- void tcp_write_timer_handler(struct sock *sk);
- void tcp_delack_timer_handler(struct sock *sk);
--int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg);
-+int tcp_ioctl(struct sock *sk, int cmd, int *karg);
- int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb);
- void tcp_rcv_established(struct sock *sk, struct sk_buff *skb);
- void tcp_rcv_space_adjust(struct sock *sk);
-diff --git a/include/net/udp.h b/include/net/udp.h
-index fa4cdbe55552c..b5e5d5e2b4e1b 100644
---- a/include/net/udp.h
-+++ b/include/net/udp.h
-@@ -275,7 +275,7 @@ void udp_flush_pending_frames(struct sock *sk);
- int udp_cmsg_send(struct sock *sk, struct msghdr *msg, u16 *gso_size);
- void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst);
- int udp_rcv(struct sk_buff *skb);
--int udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
-+int udp_ioctl(struct sock *sk, int cmd, int *karg);
- int udp_init_sock(struct sock *sk);
- int udp_pre_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len);
- int __udp_disconnect(struct sock *sk, int flags);
-diff --git a/net/core/sock.c b/net/core/sock.c
-index c8803b95ea0da..e55220cc08640 100644
---- a/net/core/sock.c
-+++ b/net/core/sock.c
-@@ -115,6 +115,9 @@
- #include <linux/memcontrol.h>
- #include <linux/prefetch.h>
- #include <linux/compat.h>
-+#include <linux/mroute.h>
-+#include <linux/mroute6.h>
-+#include <linux/icmpv6.h>
-
- #include <linux/uaccess.h>
-
-@@ -139,6 +142,7 @@
-
- #include <net/tcp.h>
- #include <net/busy_poll.h>
-+#include <net/phonet/phonet.h>
-
- #include <linux/ethtool.h>
-
-@@ -4129,3 +4133,63 @@ int sock_bind_add(struct sock *sk, struct sockaddr *addr, int addr_len)
- return sk->sk_prot->bind_add(sk, addr, addr_len);
- }
- EXPORT_SYMBOL(sock_bind_add);
-+
-+/* Copy 'size' bytes from userspace and return `size` back to userspace */
-+int sock_ioctl_inout(struct sock *sk, unsigned int cmd,
-+ void __user *arg, void *karg, size_t size)
-+{
-+ int ret;
-+
-+ if (copy_from_user(karg, arg, size))
-+ return -EFAULT;
-+
-+ ret = READ_ONCE(sk->sk_prot)->ioctl(sk, cmd, karg);
-+ if (ret)
-+ return ret;
-+
-+ if (copy_to_user(arg, karg, size))
-+ return -EFAULT;
-+
-+ return 0;
-+}
-+EXPORT_SYMBOL(sock_ioctl_inout);
-+
-+/* This is the most common ioctl prep function, where the result (4 bytes) is
-+ * copied back to userspace if the ioctl() returns successfully. No input is
-+ * copied from userspace as input argument.
-+ */
-+static int sock_ioctl_out(struct sock *sk, unsigned int cmd, void __user *arg)
-+{
-+ int ret, karg = 0;
-+
-+ ret = READ_ONCE(sk->sk_prot)->ioctl(sk, cmd, &karg);
-+ if (ret)
-+ return ret;
-+
-+ return put_user(karg, (int __user *)arg);
-+}
-+
-+/* A wrapper around sock ioctls, which copies the data from userspace
-+ * (depending on the protocol/ioctl), and copies back the result to userspace.
-+ * The main motivation for this function is to pass kernel memory to the
-+ * protocol ioctl callbacks, instead of userspace memory.
-+ */
-+int sk_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
-+{
-+ int rc = 1;
-+
-+ if (sk_is_ipmr(sk))
-+ rc = ipmr_sk_ioctl(sk, cmd, arg);
-+ else if (sk_is_icmpv6(sk))
-+ rc = ip6mr_sk_ioctl(sk, cmd, arg);
-+ else if (sk_is_phonet(sk))
-+ rc = phonet_sk_ioctl(sk, cmd, arg);
-+
-+ /* If ioctl was processed, returns its value */
-+ if (rc <= 0)
-+ return rc;
-+
-+ /* Otherwise call the default handler */
-+ return sock_ioctl_out(sk, cmd, arg);
-+}
-+EXPORT_SYMBOL(sk_ioctl);
-diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h
-index 9ddc3a9e89e40..1f748ed1279d3 100644
---- a/net/dccp/dccp.h
-+++ b/net/dccp/dccp.h
-@@ -292,7 +292,7 @@ int dccp_getsockopt(struct sock *sk, int level, int optname,
- char __user *optval, int __user *optlen);
- int dccp_setsockopt(struct sock *sk, int level, int optname,
- sockptr_t optval, unsigned int optlen);
--int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg);
-+int dccp_ioctl(struct sock *sk, int cmd, int *karg);
- int dccp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size);
- int dccp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags,
- int *addr_len);
-diff --git a/net/dccp/proto.c b/net/dccp/proto.c
-index c522c76a9f89f..1d25cfcdbe5dc 100644
---- a/net/dccp/proto.c
-+++ b/net/dccp/proto.c
-@@ -363,7 +363,7 @@ __poll_t dccp_poll(struct file *file, struct socket *sock,
- }
- EXPORT_SYMBOL_GPL(dccp_poll);
-
--int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
-+int dccp_ioctl(struct sock *sk, int cmd, int *karg)
- {
- int rc = -ENOTCONN;
-
-@@ -374,17 +374,17 @@ int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
-
- switch (cmd) {
- case SIOCOUTQ: {
-- int amount = sk_wmem_alloc_get(sk);
-+ *karg = sk_wmem_alloc_get(sk);
- /* Using sk_wmem_alloc here because sk_wmem_queued is not used by DCCP and
- * always 0, comparably to UDP.
- */
-
-- rc = put_user(amount, (int __user *)arg);
-+ rc = 0;
- }
- break;
- case SIOCINQ: {
- struct sk_buff *skb;
-- unsigned long amount = 0;
-+ *karg = 0;
-
- skb = skb_peek(&sk->sk_receive_queue);
- if (skb != NULL) {
-@@ -392,9 +392,9 @@ int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
- * We will only return the amount of this packet since
- * that is all that will be read.
- */
-- amount = skb->len;
-+ *karg = skb->len;
- }
-- rc = put_user(amount, (int __user *)arg);
-+ rc = 0;
- }
- break;
- default:
-diff --git a/net/ieee802154/socket.c b/net/ieee802154/socket.c
-index 1fa2fe041ec03..9c124705120dd 100644
---- a/net/ieee802154/socket.c
-+++ b/net/ieee802154/socket.c
-@@ -162,7 +162,7 @@ static int ieee802154_sock_ioctl(struct socket *sock, unsigned int cmd,
- default:
- if (!sk->sk_prot->ioctl)
- return -ENOIOCTLCMD;
-- return sk->sk_prot->ioctl(sk, cmd, arg);
-+ return sk_ioctl(sk, cmd, (void __user *)arg);
- }
- }
-
-@@ -531,22 +531,21 @@ static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
- return err;
- }
-
--static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
-+static int dgram_ioctl(struct sock *sk, int cmd, int *karg)
- {
- switch (cmd) {
- case SIOCOUTQ:
- {
-- int amount = sk_wmem_alloc_get(sk);
-+ *karg = sk_wmem_alloc_get(sk);
-
-- return put_user(amount, (int __user *)arg);
-+ return 0;
- }
-
- case SIOCINQ:
- {
- struct sk_buff *skb;
-- unsigned long amount;
-
-- amount = 0;
-+ *karg = 0;
- spin_lock_bh(&sk->sk_receive_queue.lock);
- skb = skb_peek(&sk->sk_receive_queue);
- if (skb) {
-@@ -554,10 +553,10 @@ static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
- * of this packet since that is all
- * that will be read.
- */
-- amount = skb->len - ieee802154_hdr_length(skb);
-+ *karg = skb->len - ieee802154_hdr_length(skb);
- }
- spin_unlock_bh(&sk->sk_receive_queue.lock);
-- return put_user(amount, (int __user *)arg);
-+ return 0;
- }
- }
-
-diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
-index 9408dc3bb42d3..eddedc2f8c5c9 100644
---- a/net/ipv4/af_inet.c
-+++ b/net/ipv4/af_inet.c
-@@ -1007,7 +1007,7 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
- break;
- default:
- if (sk->sk_prot->ioctl)
-- err = sk->sk_prot->ioctl(sk, cmd, arg);
-+ err = sk_ioctl(sk, cmd, (void __user *)arg);
- else
- err = -ENOIOCTLCMD;
- break;
-diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
-index d5421c38c2aae..e20f7ab8099ad 100644
---- a/net/ipv4/ipmr.c
-+++ b/net/ipv4/ipmr.c
-@@ -1546,6 +1546,28 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval,
- return ret;
- }
-
-+/* Execute if this ioctl is a special mroute ioctl */
-+int ipmr_sk_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
-+{
-+ switch (cmd) {
-+ /* These userspace buffers will be consumed by ipmr_ioctl() */
-+ case SIOCGETVIFCNT: {
-+ struct sioc_vif_req buffer;
-+
-+ return sock_ioctl_inout(sk, cmd, arg, &buffer,
-+ sizeof(buffer));
-+ }
-+ case SIOCGETSGCNT: {
-+ struct sioc_sg_req buffer;
-+
-+ return sock_ioctl_inout(sk, cmd, arg, &buffer,
-+ sizeof(buffer));
-+ }
-+ }
-+ /* return code > 0 means that the ioctl was not executed */
-+ return 1;
-+}
-+
- /* Getsock opt support for the multicast routing system. */
- int ip_mroute_getsockopt(struct sock *sk, int optname, sockptr_t optval,
- sockptr_t optlen)
-@@ -1592,13 +1614,13 @@ int ip_mroute_getsockopt(struct sock *sk, int optname, sockptr_t optval,
- }
-
- /* The IP multicast ioctl support routines. */
--int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
-+int ipmr_ioctl(struct sock *sk, int cmd, void *arg)
- {
-- struct sioc_sg_req sr;
-- struct sioc_vif_req vr;
- struct vif_device *vif;
- struct mfc_cache *c;
- struct net *net = sock_net(sk);
-+ struct sioc_vif_req *vr;
-+ struct sioc_sg_req *sr;
- struct mr_table *mrt;
-
- mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
-@@ -1607,40 +1629,33 @@ int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
-
- switch (cmd) {
- case SIOCGETVIFCNT:
-- if (copy_from_user(&vr, arg, sizeof(vr)))
-- return -EFAULT;
-- if (vr.vifi >= mrt->maxvif)
-+ vr = (struct sioc_vif_req *)arg;
-+ if (vr->vifi >= mrt->maxvif)
- return -EINVAL;
-- vr.vifi = array_index_nospec(vr.vifi, mrt->maxvif);
-+ vr->vifi = array_index_nospec(vr->vifi, mrt->maxvif);
- rcu_read_lock();
-- vif = &mrt->vif_table[vr.vifi];
-- if (VIF_EXISTS(mrt, vr.vifi)) {
-- vr.icount = READ_ONCE(vif->pkt_in);
-- vr.ocount = READ_ONCE(vif->pkt_out);
-- vr.ibytes = READ_ONCE(vif->bytes_in);
-- vr.obytes = READ_ONCE(vif->bytes_out);
-+ vif = &mrt->vif_table[vr->vifi];
-+ if (VIF_EXISTS(mrt, vr->vifi)) {
-+ vr->icount = READ_ONCE(vif->pkt_in);
-+ vr->ocount = READ_ONCE(vif->pkt_out);
-+ vr->ibytes = READ_ONCE(vif->bytes_in);
-+ vr->obytes = READ_ONCE(vif->bytes_out);
- rcu_read_unlock();
-
-- if (copy_to_user(arg, &vr, sizeof(vr)))
-- return -EFAULT;
- return 0;
- }
- rcu_read_unlock();
- return -EADDRNOTAVAIL;
- case SIOCGETSGCNT:
-- if (copy_from_user(&sr, arg, sizeof(sr)))
-- return -EFAULT;
-+ sr = (struct sioc_sg_req *)arg;
-
- rcu_read_lock();
-- c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr);
-+ c = ipmr_cache_find(mrt, sr->src.s_addr, sr->grp.s_addr);
- if (c) {
-- sr.pktcnt = c->_c.mfc_un.res.pkt;
-- sr.bytecnt = c->_c.mfc_un.res.bytes;
-- sr.wrong_if = c->_c.mfc_un.res.wrong_if;
-+ sr->pktcnt = c->_c.mfc_un.res.pkt;
-+ sr->bytecnt = c->_c.mfc_un.res.bytes;
-+ sr->wrong_if = c->_c.mfc_un.res.wrong_if;
- rcu_read_unlock();
--
-- if (copy_to_user(arg, &sr, sizeof(sr)))
-- return -EFAULT;
- return 0;
- }
- rcu_read_unlock();
-diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
-index 7c63b91edbf7a..a2d1f1ee7df59 100644
---- a/net/ipv4/raw.c
-+++ b/net/ipv4/raw.c
-@@ -858,29 +858,29 @@ static int raw_getsockopt(struct sock *sk, int level, int optname,
- return do_raw_getsockopt(sk, level, optname, optval, optlen);
- }
-
--static int raw_ioctl(struct sock *sk, int cmd, unsigned long arg)
-+static int raw_ioctl(struct sock *sk, int cmd, int *karg)
- {
- switch (cmd) {
- case SIOCOUTQ: {
-- int amount = sk_wmem_alloc_get(sk);
--
-- return put_user(amount, (int __user *)arg);
-+ *karg = sk_wmem_alloc_get(sk);
-+ return 0;
- }
- case SIOCINQ: {
- struct sk_buff *skb;
-- int amount = 0;
-
- spin_lock_bh(&sk->sk_receive_queue.lock);
- skb = skb_peek(&sk->sk_receive_queue);
- if (skb)
-- amount = skb->len;
-+ *karg = skb->len;
-+ else
-+ *karg = 0;
- spin_unlock_bh(&sk->sk_receive_queue.lock);
-- return put_user(amount, (int __user *)arg);
-+ return 0;
- }
-
- default:
- #ifdef CONFIG_IP_MROUTE
-- return ipmr_ioctl(sk, cmd, (void __user *)arg);
-+ return ipmr_ioctl(sk, cmd, karg);
- #else
- return -ENOIOCTLCMD;
- #endif
-diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
-index 86e7695d91adf..dcbcf238bf3ab 100644
---- a/net/ipv4/tcp.c
-+++ b/net/ipv4/tcp.c
-@@ -598,7 +598,7 @@ __poll_t tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
- }
- EXPORT_SYMBOL(tcp_poll);
-
--int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
-+int tcp_ioctl(struct sock *sk, int cmd, int *karg)
- {
- struct tcp_sock *tp = tcp_sk(sk);
- int answ;
-@@ -640,7 +640,8 @@ int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
- return -ENOIOCTLCMD;
- }
-
-- return put_user(answ, (int __user *)arg);
-+ *karg = answ;
-+ return 0;
- }
- EXPORT_SYMBOL(tcp_ioctl);
-
-diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
-index 87d759bab0012..2a5748c246e24 100644
---- a/net/ipv4/udp.c
-+++ b/net/ipv4/udp.c
-@@ -1694,21 +1694,19 @@ static int first_packet_length(struct sock *sk)
- * IOCTL requests applicable to the UDP protocol
- */
-
--int udp_ioctl(struct sock *sk, int cmd, unsigned long arg)
-+int udp_ioctl(struct sock *sk, int cmd, int *karg)
- {
- switch (cmd) {
- case SIOCOUTQ:
- {
-- int amount = sk_wmem_alloc_get(sk);
--
-- return put_user(amount, (int __user *)arg);
-+ *karg = sk_wmem_alloc_get(sk);
-+ return 0;
- }
-
- case SIOCINQ:
- {
-- int amount = max_t(int, 0, first_packet_length(sk));
--
-- return put_user(amount, (int __user *)arg);
-+ *karg = max_t(int, 0, first_packet_length(sk));
-+ return 0;
- }
-
- default:
-diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
-index 0b42eb8c55aaf..37190217d650c 100644
---- a/net/ipv6/af_inet6.c
-+++ b/net/ipv6/af_inet6.c
-@@ -598,7 +598,7 @@ int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
- prot = READ_ONCE(sk->sk_prot);
- if (!prot->ioctl)
- return -ENOIOCTLCMD;
-- return prot->ioctl(sk, cmd, arg);
-+ return sk_ioctl(sk, cmd, (void __user *)arg);
- }
- /*NOTREACHED*/
- return 0;
-diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
-index 27fb5479988af..76db80a7124c8 100644
---- a/net/ipv6/ip6mr.c
-+++ b/net/ipv6/ip6mr.c
-@@ -1879,11 +1879,10 @@ int ip6_mroute_getsockopt(struct sock *sk, int optname, sockptr_t optval,
- /*
- * The IP multicast ioctl support routines.
- */
--
--int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg)
-+int ip6mr_ioctl(struct sock *sk, int cmd, void *arg)
- {
-- struct sioc_sg_req6 sr;
-- struct sioc_mif_req6 vr;
-+ struct sioc_sg_req6 *sr;
-+ struct sioc_mif_req6 *vr;
- struct vif_device *vif;
- struct mfc6_cache *c;
- struct net *net = sock_net(sk);
-@@ -1895,40 +1894,33 @@ int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg)
-
- switch (cmd) {
- case SIOCGETMIFCNT_IN6:
-- if (copy_from_user(&vr, arg, sizeof(vr)))
-- return -EFAULT;
-- if (vr.mifi >= mrt->maxvif)
-+ vr = (struct sioc_mif_req6 *)arg;
-+ if (vr->mifi >= mrt->maxvif)
- return -EINVAL;
-- vr.mifi = array_index_nospec(vr.mifi, mrt->maxvif);
-+ vr->mifi = array_index_nospec(vr->mifi, mrt->maxvif);
- rcu_read_lock();
-- vif = &mrt->vif_table[vr.mifi];
-- if (VIF_EXISTS(mrt, vr.mifi)) {
-- vr.icount = READ_ONCE(vif->pkt_in);
-- vr.ocount = READ_ONCE(vif->pkt_out);
-- vr.ibytes = READ_ONCE(vif->bytes_in);
-- vr.obytes = READ_ONCE(vif->bytes_out);
-+ vif = &mrt->vif_table[vr->mifi];
-+ if (VIF_EXISTS(mrt, vr->mifi)) {
-+ vr->icount = READ_ONCE(vif->pkt_in);
-+ vr->ocount = READ_ONCE(vif->pkt_out);
-+ vr->ibytes = READ_ONCE(vif->bytes_in);
-+ vr->obytes = READ_ONCE(vif->bytes_out);
- rcu_read_unlock();
--
-- if (copy_to_user(arg, &vr, sizeof(vr)))
-- return -EFAULT;
- return 0;
- }
- rcu_read_unlock();
- return -EADDRNOTAVAIL;
- case SIOCGETSGCNT_IN6:
-- if (copy_from_user(&sr, arg, sizeof(sr)))
-- return -EFAULT;
-+ sr = (struct sioc_sg_req6 *)arg;
-
- rcu_read_lock();
-- c = ip6mr_cache_find(mrt, &sr.src.sin6_addr, &sr.grp.sin6_addr);
-+ c = ip6mr_cache_find(mrt, &sr->src.sin6_addr,
-+ &sr->grp.sin6_addr);
- if (c) {
-- sr.pktcnt = c->_c.mfc_un.res.pkt;
-- sr.bytecnt = c->_c.mfc_un.res.bytes;
-- sr.wrong_if = c->_c.mfc_un.res.wrong_if;
-+ sr->pktcnt = c->_c.mfc_un.res.pkt;
-+ sr->bytecnt = c->_c.mfc_un.res.bytes;
-+ sr->wrong_if = c->_c.mfc_un.res.wrong_if;
- rcu_read_unlock();
--
-- if (copy_to_user(arg, &sr, sizeof(sr)))
-- return -EFAULT;
- return 0;
- }
- rcu_read_unlock();
-diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
-index dc31752a7edcc..e361953ca7f45 100644
---- a/net/ipv6/raw.c
-+++ b/net/ipv6/raw.c
-@@ -1116,29 +1116,29 @@ static int rawv6_getsockopt(struct sock *sk, int level, int optname,
- return do_rawv6_getsockopt(sk, level, optname, optval, optlen);
- }
-
--static int rawv6_ioctl(struct sock *sk, int cmd, unsigned long arg)
-+static int rawv6_ioctl(struct sock *sk, int cmd, int *karg)
- {
- switch (cmd) {
- case SIOCOUTQ: {
-- int amount = sk_wmem_alloc_get(sk);
--
-- return put_user(amount, (int __user *)arg);
-+ *karg = sk_wmem_alloc_get(sk);
-+ return 0;
- }
- case SIOCINQ: {
- struct sk_buff *skb;
-- int amount = 0;
-
- spin_lock_bh(&sk->sk_receive_queue.lock);
- skb = skb_peek(&sk->sk_receive_queue);
- if (skb)
-- amount = skb->len;
-+ *karg = skb->len;
-+ else
-+ *karg = 0;
- spin_unlock_bh(&sk->sk_receive_queue.lock);
-- return put_user(amount, (int __user *)arg);
-+ return 0;
- }
-
- default:
- #ifdef CONFIG_IPV6_MROUTE
-- return ip6mr_ioctl(sk, cmd, (void __user *)arg);
-+ return ip6mr_ioctl(sk, cmd, karg);
- #else
- return -ENOIOCTLCMD;
- #endif
-diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h
-index a88e070b431d8..91ebf0a3f4997 100644
---- a/net/l2tp/l2tp_core.h
-+++ b/net/l2tp/l2tp_core.h
-@@ -272,7 +272,7 @@ int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops
- void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type);
-
- /* IOCTL helper for IP encap modules. */
--int l2tp_ioctl(struct sock *sk, int cmd, unsigned long arg);
-+int l2tp_ioctl(struct sock *sk, int cmd, int *karg);
-
- /* Extract the tunnel structure from a socket's sk_user_data pointer,
- * validating the tunnel magic feather.
-diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c
-index 41a74fc84ca13..2b795c1064f5c 100644
---- a/net/l2tp/l2tp_ip.c
-+++ b/net/l2tp/l2tp_ip.c
-@@ -562,19 +562,18 @@ static int l2tp_ip_recvmsg(struct sock *sk, struct msghdr *msg,
- return err ? err : copied;
- }
-
--int l2tp_ioctl(struct sock *sk, int cmd, unsigned long arg)
-+int l2tp_ioctl(struct sock *sk, int cmd, int *karg)
- {
- struct sk_buff *skb;
-- int amount;
-
- switch (cmd) {
- case SIOCOUTQ:
-- amount = sk_wmem_alloc_get(sk);
-+ *karg = sk_wmem_alloc_get(sk);
- break;
- case SIOCINQ:
- spin_lock_bh(&sk->sk_receive_queue.lock);
- skb = skb_peek(&sk->sk_receive_queue);
-- amount = skb ? skb->len : 0;
-+ *karg = skb ? skb->len : 0;
- spin_unlock_bh(&sk->sk_receive_queue.lock);
- break;
-
-@@ -582,7 +581,7 @@ int l2tp_ioctl(struct sock *sk, int cmd, unsigned long arg)
- return -ENOIOCTLCMD;
- }
-
-- return put_user(amount, (int __user *)arg);
-+ return 0;
- }
- EXPORT_SYMBOL_GPL(l2tp_ioctl);
-
-diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
-index 859b18cb8e4f6..7ea10d7aec961 100644
---- a/net/mptcp/protocol.c
-+++ b/net/mptcp/protocol.c
-@@ -3640,11 +3640,10 @@ static int mptcp_ioctl_outq(const struct mptcp_sock *msk, u64 v)
- return (int)delta;
- }
-
--static int mptcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
-+static int mptcp_ioctl(struct sock *sk, int cmd, int *karg)
- {
- struct mptcp_sock *msk = mptcp_sk(sk);
- bool slow;
-- int answ;
-
- switch (cmd) {
- case SIOCINQ:
-@@ -3653,24 +3652,24 @@ static int mptcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
-
- lock_sock(sk);
- __mptcp_move_skbs(msk);
-- answ = mptcp_inq_hint(sk);
-+ *karg = mptcp_inq_hint(sk);
- release_sock(sk);
- break;
- case SIOCOUTQ:
- slow = lock_sock_fast(sk);
-- answ = mptcp_ioctl_outq(msk, READ_ONCE(msk->snd_una));
-+ *karg = mptcp_ioctl_outq(msk, READ_ONCE(msk->snd_una));
- unlock_sock_fast(sk, slow);
- break;
- case SIOCOUTQNSD:
- slow = lock_sock_fast(sk);
-- answ = mptcp_ioctl_outq(msk, msk->snd_nxt);
-+ *karg = mptcp_ioctl_outq(msk, msk->snd_nxt);
- unlock_sock_fast(sk, slow);
- break;
- default:
- return -ENOIOCTLCMD;
- }
-
-- return put_user(answ, (int __user *)arg);
-+ return 0;
- }
-
- static void mptcp_subflow_early_fallback(struct mptcp_sock *msk,
-diff --git a/net/phonet/datagram.c b/net/phonet/datagram.c
-index ff5f49ab236ed..3aa50dc7535b7 100644
---- a/net/phonet/datagram.c
-+++ b/net/phonet/datagram.c
-@@ -28,24 +28,21 @@ static void pn_sock_close(struct sock *sk, long timeout)
- sk_common_release(sk);
- }
-
--static int pn_ioctl(struct sock *sk, int cmd, unsigned long arg)
-+static int pn_ioctl(struct sock *sk, int cmd, int *karg)
- {
- struct sk_buff *skb;
-- int answ;
-
- switch (cmd) {
- case SIOCINQ:
- lock_sock(sk);
- skb = skb_peek(&sk->sk_receive_queue);
-- answ = skb ? skb->len : 0;
-+ *karg = skb ? skb->len : 0;
- release_sock(sk);
-- return put_user(answ, (int __user *)arg);
-+ return 0;
-
- case SIOCPNADDRESOURCE:
- case SIOCPNDELRESOURCE: {
-- u32 res;
-- if (get_user(res, (u32 __user *)arg))
-- return -EFAULT;
-+ u32 res = *karg;
- if (res >= 256)
- return -EINVAL;
- if (cmd == SIOCPNADDRESOURCE)
-diff --git a/net/phonet/pep.c b/net/phonet/pep.c
-index 83ea13a50690b..faba31f2eff29 100644
---- a/net/phonet/pep.c
-+++ b/net/phonet/pep.c
-@@ -917,10 +917,9 @@ static int pep_sock_enable(struct sock *sk, struct sockaddr *addr, int len)
- return 0;
- }
-
--static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg)
-+static int pep_ioctl(struct sock *sk, int cmd, int *karg)
- {
- struct pep_sock *pn = pep_sk(sk);
-- int answ;
- int ret = -ENOIOCTLCMD;
-
- switch (cmd) {
-@@ -933,13 +932,13 @@ static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg)
- lock_sock(sk);
- if (sock_flag(sk, SOCK_URGINLINE) &&
- !skb_queue_empty(&pn->ctrlreq_queue))
-- answ = skb_peek(&pn->ctrlreq_queue)->len;
-+ *karg = skb_peek(&pn->ctrlreq_queue)->len;
- else if (!skb_queue_empty(&sk->sk_receive_queue))
-- answ = skb_peek(&sk->sk_receive_queue)->len;
-+ *karg = skb_peek(&sk->sk_receive_queue)->len;
- else
-- answ = 0;
-+ *karg = 0;
- release_sock(sk);
-- ret = put_user(answ, (int __user *)arg);
-+ ret = 0;
- break;
-
- case SIOCPNENABLEPIPE:
-diff --git a/net/phonet/socket.c b/net/phonet/socket.c
-index 71e2caf6ab859..967f9b4dc0265 100644
---- a/net/phonet/socket.c
-+++ b/net/phonet/socket.c
-@@ -387,7 +387,7 @@ static int pn_socket_ioctl(struct socket *sock, unsigned int cmd,
- return put_user(handle, (__u16 __user *)arg);
- }
-
-- return sk->sk_prot->ioctl(sk, cmd, arg);
-+ return sk_ioctl(sk, cmd, (void __user *)arg);
- }
-
- static int pn_socket_listen(struct socket *sock, int backlog)
-diff --git a/net/sctp/socket.c b/net/sctp/socket.c
-index 237a6b04adf6f..9c29bff647a11 100644
---- a/net/sctp/socket.c
-+++ b/net/sctp/socket.c
-@@ -4902,7 +4902,7 @@ static struct sock *sctp_accept(struct sock *sk, int flags, int *err, bool kern)
- }
-
- /* The SCTP ioctl handler. */
--static int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
-+static int sctp_ioctl(struct sock *sk, int cmd, int *karg)
- {
- int rc = -ENOTCONN;
-
-@@ -4918,7 +4918,7 @@ static int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
- switch (cmd) {
- case SIOCINQ: {
- struct sk_buff *skb;
-- unsigned int amount = 0;
-+ *karg = 0;
-
- skb = skb_peek(&sk->sk_receive_queue);
- if (skb != NULL) {
-@@ -4926,9 +4926,9 @@ static int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
- * We will only return the amount of this packet since
- * that is all that will be read.
- */
-- amount = skb->len;
-+ *karg = skb->len;
- }
-- rc = put_user(amount, (int __user *)arg);
-+ rc = 0;
- break;
- }
- default:
---
-2.43.0
-