[PATCH net-next 4/6] ipv4: convert do_ip_getsockopt() to sockopt_t
From: Breno Leitao
Date: Fri Sep 25 2026 - 11:59:25 EST
Convert the SOL_IP switch and ip_mroute_getsockopt(), its last leaf on
a sockptr_t pair. The IP_MSFILTER and MCAST_MSFILTER bridges go away.
ip_getsockopt() builds the sockopt_t with sockopt_init_user() and
writes optlen back unconditionally, because IP_OPTIONS and others
report a length then fail; ip_mc_msfget() therefore stores its length
last. IP_PKTOPTIONS takes its buffer off iter_out. The argument is
sopt, since IP_OPTIONS already has an ip_options local named opt.
Validating optlen now precedes the level check and the MRT_* dispatch,
so a negative optlen wins over -EOPNOTSUPP there.
Signed-off-by: Breno Leitao <leitao@xxxxxxxxxx>
---
include/linux/mroute.h | 4 +--
include/net/ip.h | 3 +-
net/core/filter.c | 18 ++++++++---
net/ipv4/igmp.c | 3 +-
net/ipv4/ip_sockglue.c | 87 +++++++++++++++++++-------------------------------
net/ipv4/ipmr.c | 11 +++----
6 files changed, 56 insertions(+), 70 deletions(-)
diff --git a/include/linux/mroute.h b/include/linux/mroute.h
index 4c5003afee6c51..c1c21e61c81c37 100644
--- a/include/linux/mroute.h
+++ b/include/linux/mroute.h
@@ -17,7 +17,7 @@ static inline int ip_mroute_opt(int opt)
}
int ip_mroute_setsockopt(struct sock *, int, sockptr_t, unsigned int);
-int ip_mroute_getsockopt(struct sock *, int, sockptr_t, sockptr_t);
+int ip_mroute_getsockopt(struct sock *sk, int optname, sockopt_t *opt);
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);
@@ -31,7 +31,7 @@ static inline int ip_mroute_setsockopt(struct sock *sock, int optname,
}
static inline int ip_mroute_getsockopt(struct sock *sk, int optname,
- sockptr_t optval, sockptr_t optlen)
+ sockopt_t *opt)
{
return -ENOPROTOOPT;
}
diff --git a/include/net/ip.h b/include/net/ip.h
index 6f602df72ee621..81aef4b98430bf 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -823,8 +823,7 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
unsigned int optlen);
int ip_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
unsigned int optlen);
-int do_ip_getsockopt(struct sock *sk, int level, int optname,
- sockptr_t optval, sockptr_t optlen);
+int do_ip_getsockopt(struct sock *sk, int level, int optname, sockopt_t *sopt);
int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval,
int __user *optlen);
int ip_ra_control(struct sock *sk, unsigned char on,
diff --git a/net/core/filter.c b/net/core/filter.c
index 70dc621672f2e9..9a706ec1f7f122 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -5737,10 +5737,20 @@ static int sol_ip_sockopt(struct sock *sk, int optname,
return -EINVAL;
}
- if (getopt)
- return do_ip_getsockopt(sk, SOL_IP, optname,
- KERNEL_SOCKPTR(optval),
- KERNEL_SOCKPTR(optlen));
+ if (getopt) {
+ struct kvec kvec;
+ sockopt_t opt;
+ int err;
+
+ err = sockptr_to_sockopt(&opt, KERNEL_SOCKPTR(optval),
+ KERNEL_SOCKPTR(optlen), &kvec);
+ if (err)
+ return err;
+
+ err = do_ip_getsockopt(sk, SOL_IP, optname, &opt);
+ *optlen = opt.optlen;
+ return err;
+ }
return do_ip_setsockopt(sk, SOL_IP, optname,
KERNEL_SOCKPTR(optval), *optlen);
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index d573c5bf8f038b..bf00810cdd7a13 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -2763,12 +2763,13 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, sockopt_t *opt)
if (err)
return err;
- opt->optlen = msf_size;
if (copy_to_iter(msf, IP_MSFILTER_SIZE(0), &opt->iter_out) !=
IP_MSFILTER_SIZE(0))
return -EFAULT;
if (len && copy_to_iter(psl->sl_addr, len, &opt->iter_out) != len)
return -EFAULT;
+
+ opt->optlen = msf_size;
return 0;
done:
return err;
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 1f452b6ea86e9d..2e78b11d294a9f 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -1509,8 +1509,7 @@ static int compat_ip_get_mcast_msfilter(struct sock *sk, sockopt_t *opt)
return 0;
}
-int do_ip_getsockopt(struct sock *sk, int level, int optname,
- sockptr_t optval, sockptr_t optlen)
+int do_ip_getsockopt(struct sock *sk, int level, int optname, sockopt_t *sopt)
{
struct inet_sock *inet = inet_sk(sk);
bool needs_rtnl = getsockopt_needs_rtnl(optname);
@@ -1521,10 +1520,9 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
return -EOPNOTSUPP;
if (ip_mroute_opt(optname))
- return ip_mroute_getsockopt(sk, optname, optval, optlen);
+ return ip_mroute_getsockopt(sk, optname, sopt);
- if (copy_from_sockptr(&len, optlen, sizeof(int)))
- return -EFAULT;
+ len = sopt->optlen;
if (len < 0)
return -EINVAL;
@@ -1620,16 +1618,15 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
rcu_read_unlock();
if (opt->optlen == 0) {
- len = 0;
- return copy_to_sockptr(optlen, &len, sizeof(int));
+ sopt->optlen = 0;
+ return 0;
}
ip_options_undo(opt);
len = min_t(unsigned int, len, opt->optlen);
- if (copy_to_sockptr(optlen, &len, sizeof(int)))
- return -EFAULT;
- if (copy_to_sockptr(optval, opt->__data, len))
+ sopt->optlen = len;
+ if (copy_to_iter(opt->__data, len, &sopt->iter_out) != len)
return -EFAULT;
return 0;
}
@@ -1653,12 +1650,12 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
if (sk->sk_type != SOCK_STREAM)
return -ENOPROTOOPT;
- if (optval.is_kernel) {
+ if (iov_iter_is_kvec(&sopt->iter_out)) {
msg.msg_control_is_user = false;
- msg.msg_control = optval.kernel;
+ msg.msg_control = sopt->iter_out.kvec->iov_base;
} else {
msg.msg_control_is_user = true;
- msg.msg_control_user = optval.user;
+ msg.msg_control_user = sopt->iter_out.ubuf;
}
msg.msg_controllen = len;
msg.msg_flags = in_compat_syscall() ? MSG_CMSG_COMPAT : 0;
@@ -1680,8 +1677,8 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
int tos = READ_ONCE(inet->rcv_tos);
put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos);
}
- len -= msg.msg_controllen;
- return copy_to_sockptr(optlen, &len, sizeof(int));
+ sopt->optlen = len - msg.msg_controllen;
+ return 0;
}
case IP_UNICAST_IF:
val = (__force int)htonl((__u32) READ_ONCE(inet->uc_index));
@@ -1692,9 +1689,8 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
len = min_t(unsigned int, len, sizeof(struct in_addr));
addr.s_addr = READ_ONCE(inet->mc_addr);
- if (copy_to_sockptr(optlen, &len, sizeof(int)))
- return -EFAULT;
- if (copy_to_sockptr(optval, &addr, len))
+ sopt->optlen = len;
+ if (copy_to_iter(&addr, len, &sopt->iter_out) != len)
return -EFAULT;
return 0;
}
@@ -1711,43 +1707,25 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
case IP_MSFILTER:
{
struct ip_msfilter msf;
- struct kvec kvec;
- sockopt_t opt;
if (len < IP_MSFILTER_SIZE(0)) {
err = -EINVAL;
goto out;
}
- if (copy_from_sockptr(&msf, optval, IP_MSFILTER_SIZE(0))) {
+ if (copy_from_iter(&msf, IP_MSFILTER_SIZE(0), &sopt->iter_in) !=
+ IP_MSFILTER_SIZE(0)) {
err = -EFAULT;
goto out;
}
- err = sockptr_to_sockopt(&opt, optval, optlen, &kvec);
- if (err)
- goto out;
-
- err = ip_mc_msfget(sk, &msf, &opt);
- if (!err && copy_to_sockptr(optlen, &opt.optlen, sizeof(int)))
- err = -EFAULT;
+ err = ip_mc_msfget(sk, &msf, sopt);
goto out;
}
case MCAST_MSFILTER:
- {
- struct kvec kvec;
- sockopt_t opt;
-
- err = sockptr_to_sockopt(&opt, optval, optlen, &kvec);
- if (err)
- goto out;
-
if (in_compat_syscall())
- err = compat_ip_get_mcast_msfilter(sk, &opt);
+ err = compat_ip_get_mcast_msfilter(sk, sopt);
else
- err = ip_get_mcast_msfilter(sk, &opt);
- if (!err && copy_to_sockptr(optlen, &opt.optlen, sizeof(int)))
- err = -EFAULT;
+ err = ip_get_mcast_msfilter(sk, sopt);
goto out;
- }
case IP_PROTOCOL:
val = inet_sk(sk)->inet_num;
break;
@@ -1759,16 +1737,14 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
copyval:
if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) {
unsigned char ucval = (unsigned char)val;
- len = 1;
- if (copy_to_sockptr(optlen, &len, sizeof(int)))
- return -EFAULT;
- if (copy_to_sockptr(optval, &ucval, 1))
+
+ sopt->optlen = 1;
+ if (copy_to_iter(&ucval, 1, &sopt->iter_out) != 1)
return -EFAULT;
} else {
len = min_t(unsigned int, sizeof(int), len);
- if (copy_to_sockptr(optlen, &len, sizeof(int)))
- return -EFAULT;
- if (copy_to_sockptr(optval, &val, len))
+ sopt->optlen = len;
+ if (copy_to_iter(&val, len, &sopt->iter_out) != len)
return -EFAULT;
}
return 0;
@@ -1783,19 +1759,22 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
int ip_getsockopt(struct sock *sk, int level,
int optname, char __user *optval, int __user *optlen)
{
+ sockopt_t opt;
int err;
- err = do_ip_getsockopt(sk, level, optname,
- USER_SOCKPTR(optval), USER_SOCKPTR(optlen));
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = do_ip_getsockopt(sk, level, optname, &opt);
+ if (put_user(opt.optlen, optlen))
+ return -EFAULT;
#ifdef CONFIG_NETFILTER
/* we need to exclude all possible ENOPROTOOPTs except default case */
if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
!ip_mroute_opt(optname)) {
- int len;
-
- if (get_user(len, optlen))
- return -EFAULT;
+ int len = opt.optlen;
err = nf_getsockopt(sk, PF_INET, optname, optval, &len);
if (err >= 0)
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index e4c51ca473019c..f249569b4bf8b9 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -1638,8 +1638,7 @@ int ipmr_sk_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
}
/* Getsock opt support for the multicast routing system. */
-int ip_mroute_getsockopt(struct sock *sk, int optname, sockptr_t optval,
- sockptr_t optlen)
+int ip_mroute_getsockopt(struct sock *sk, int optname, sockopt_t *opt)
{
int olr;
int val;
@@ -1670,16 +1669,14 @@ int ip_mroute_getsockopt(struct sock *sk, int optname, sockptr_t optval,
return -ENOPROTOOPT;
}
- if (copy_from_sockptr(&olr, optlen, sizeof(int)))
- return -EFAULT;
+ olr = opt->optlen;
if (olr < 0)
return -EINVAL;
olr = min_t(unsigned int, olr, sizeof(int));
- if (copy_to_sockptr(optlen, &olr, sizeof(int)))
- return -EFAULT;
- if (copy_to_sockptr(optval, &val, olr))
+ opt->optlen = olr;
+ if (copy_to_iter(&val, olr, &opt->iter_out) != olr)
return -EFAULT;
return 0;
}
--
2.53.0-Meta