[PATCH net-next 5/6] ipv6: convert do_ipv6_getsockopt() to sockopt_t

From: Breno Leitao

Date: Fri Sep 25 2026 - 12:06:51 EST


Convert the SOL_IPV6 switch and its two remaining sockptr_t leaves,
ip6_mroute_getsockopt() and ipv6_getsockopt_sticky(). The
MCAST_MSFILTER bridge goes away.

ipv6_getsockopt() builds the sockopt_t with sockopt_init_user() after
its own level checks, and writes optlen back unconditionally.
IPV6_2292PKTOPTIONS takes its buffer off iter_out; IPV6_FLOWLABEL_MGR
reads through iter_in. The argument is sopt, since the sticky options
already have an ipv6_txoptions local named opt.

MRT6_* is still dispatched before optlen is validated, so a negative
optlen there wins over -EOPNOTSUPP.

Signed-off-by: Breno Leitao <leitao@xxxxxxxxxx>
---
include/linux/mroute6.h | 5 ++--
include/net/ipv6.h | 2 +-
net/core/filter.c | 18 ++++++++---
net/ipv6/ip6mr.c | 13 +++-----
net/ipv6/ipv6_sockglue.c | 78 ++++++++++++++++++++----------------------------
5 files changed, 53 insertions(+), 63 deletions(-)

diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h
index fddafdc168f733..ab4d206fb32065 100644
--- a/include/linux/mroute6.h
+++ b/include/linux/mroute6.h
@@ -27,7 +27,7 @@ struct sock;

#ifdef CONFIG_IPV6_MROUTE
extern int ip6_mroute_setsockopt(struct sock *, int, sockptr_t, unsigned int);
-extern int ip6_mroute_getsockopt(struct sock *, int, sockptr_t, sockptr_t);
+int ip6_mroute_getsockopt(struct sock *sk, int optname, sockopt_t *sopt);
extern int ip6_mr_input(struct sk_buff *skb);
extern int ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg);
extern int ip6_mr_init(void);
@@ -42,8 +42,7 @@ static inline int ip6_mroute_setsockopt(struct sock *sock, int optname,
}

static inline
-int ip6_mroute_getsockopt(struct sock *sock,
- int optname, sockptr_t optval, sockptr_t optlen)
+int ip6_mroute_getsockopt(struct sock *sock, int optname, sockopt_t *sopt)
{
return -ENOPROTOOPT;
}
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 9bb68d75890364..a1e1de7da8c70d 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -1142,7 +1142,7 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval
int ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
unsigned int optlen);
int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
- sockptr_t optval, sockptr_t optlen);
+ sockopt_t *sopt);
int ipv6_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen);

diff --git a/net/core/filter.c b/net/core/filter.c
index 9a706ec1f7f122..e0b79c78c9a9c8 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -5774,10 +5774,20 @@ static int sol_ipv6_sockopt(struct sock *sk, int optname,
return -EINVAL;
}

- if (getopt)
- return do_ipv6_getsockopt(sk, SOL_IPV6, 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_ipv6_getsockopt(sk, SOL_IPV6, optname, &opt);
+ *optlen = opt.optlen;
+ return err;
+ }

return do_ipv6_setsockopt(sk, SOL_IPV6, optname,
KERNEL_SOCKPTR(optval), *optlen);
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 36f117ad367081..9513258b95fb88 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -1893,8 +1893,7 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval,
* Getsock opt support for the multicast routing system.
*/

-int ip6_mroute_getsockopt(struct sock *sk, int optname, sockptr_t optval,
- sockptr_t optlen)
+int ip6_mroute_getsockopt(struct sock *sk, int optname, sockopt_t *sopt)
{
int olr;
int val;
@@ -1925,16 +1924,12 @@ int ip6_mroute_getsockopt(struct sock *sk, int optname, sockptr_t optval,
return -ENOPROTOOPT;
}

- if (copy_from_sockptr(&olr, optlen, sizeof(int)))
- return -EFAULT;
-
- olr = min_t(int, olr, sizeof(int));
+ olr = min_t(int, sopt->optlen, sizeof(int));
if (olr < 0)
return -EINVAL;

- if (copy_to_sockptr(optlen, &olr, sizeof(int)))
- return -EFAULT;
- if (copy_to_sockptr(optval, &val, olr))
+ sopt->optlen = olr;
+ if (copy_to_iter(&val, olr, &sopt->iter_out) != olr)
return -EFAULT;
return 0;
}
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 1bdb3e001e4fe6..3a43e26234eea3 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -889,7 +889,7 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
EXPORT_SYMBOL(ipv6_setsockopt);

static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,
- int optname, sockptr_t optval, int len)
+ int optname, sockopt_t *sopt, int len)
{
struct ipv6_opt_hdr *hdr;

@@ -917,7 +917,7 @@ static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,
return 0;

len = min_t(unsigned int, len, ipv6_optlen(hdr));
- if (copy_to_sockptr(optval, hdr, len))
+ if (copy_to_iter(hdr, len, &sopt->iter_out) != len)
return -EFAULT;
return len;
}
@@ -997,39 +997,23 @@ static int compat_ipv6_get_msfilter(struct sock *sk, sockopt_t *opt)
return 0;
}

-int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
- sockptr_t optval, sockptr_t optlen)
+int do_ipv6_getsockopt(struct sock *sk, int level, int optname, sockopt_t *sopt)
{
struct ipv6_pinfo *np = inet6_sk(sk);
int len;
int val;

if (ip6_mroute_opt(optname))
- return ip6_mroute_getsockopt(sk, optname, optval, optlen);
+ return ip6_mroute_getsockopt(sk, optname, sopt);

- if (copy_from_sockptr(&len, optlen, sizeof(int)))
- return -EFAULT;
+ len = sopt->optlen;
if (len < 0)
return -EINVAL;
switch (optname) {
case MCAST_MSFILTER:
- {
- struct kvec kvec;
- sockopt_t opt;
- int err;
-
- err = sockptr_to_sockopt(&opt, optval, optlen, &kvec);
- if (err)
- return err;
-
if (in_compat_syscall())
- err = compat_ipv6_get_msfilter(sk, &opt);
- else
- err = ipv6_get_msfilter(sk, &opt);
- if (!err && copy_to_sockptr(optlen, &opt.optlen, sizeof(int)))
- err = -EFAULT;
- return err;
- }
+ return compat_ipv6_get_msfilter(sk, sopt);
+ return ipv6_get_msfilter(sk, sopt);
case IPV6_2292PKTOPTIONS:
{
struct msghdr msg;
@@ -1038,12 +1022,12 @@ int do_ipv6_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 = 0;
@@ -1094,8 +1078,8 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
put_cmsg(&msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
}
}
- len -= msg.msg_controllen;
- return copy_to_sockptr(optlen, &len, sizeof(int));
+ sopt->optlen = len - msg.msg_controllen;
+ return 0;
}
case IPV6_MTU:
{
@@ -1150,12 +1134,13 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
sockopt_lock_sock(sk);
opt = rcu_dereference_protected(np->opt,
lockdep_sock_is_held(sk));
- len = ipv6_getsockopt_sticky(sk, opt, optname, optval, len);
+ len = ipv6_getsockopt_sticky(sk, opt, optname, sopt, len);
sockopt_release_sock(sk);
/* check if ipv6_getsockopt_sticky() returns err code */
if (len < 0)
return len;
- return copy_to_sockptr(optlen, &len, sizeof(int));
+ sopt->optlen = len;
+ return 0;
}

case IPV6_RECVHOPOPTS:
@@ -1209,9 +1194,8 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
if (!mtuinfo.ip6m_mtu)
return -ENOTCONN;

- if (copy_to_sockptr(optlen, &len, sizeof(int)))
- return -EFAULT;
- if (copy_to_sockptr(optval, &mtuinfo, len))
+ sopt->optlen = len;
+ if (copy_to_iter(&mtuinfo, len, &sopt->iter_out) != len)
return -EFAULT;

return 0;
@@ -1288,7 +1272,8 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
if (len < sizeof(freq))
return -EINVAL;

- if (copy_from_sockptr(&freq, optval, sizeof(freq)))
+ if (copy_from_iter(&freq, sizeof(freq), &sopt->iter_in) !=
+ sizeof(freq))
return -EFAULT;

if (freq.flr_action != IPV6_FL_A_GET)
@@ -1303,9 +1288,8 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
if (val < 0)
return val;

- if (copy_to_sockptr(optlen, &len, sizeof(int)))
- return -EFAULT;
- if (copy_to_sockptr(optval, &freq, len))
+ sopt->optlen = len;
+ if (copy_to_iter(&freq, len, &sopt->iter_out) != len)
return -EFAULT;

return 0;
@@ -1363,9 +1347,8 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
return -ENOPROTOOPT;
}
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;
}
@@ -1373,6 +1356,7 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
int ipv6_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
+ sockopt_t sopt;
int err;

if (level == SOL_IP && sk->sk_type != SOCK_RAW)
@@ -1381,15 +1365,17 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
if (level != SOL_IPV6)
return -ENOPROTOOPT;

- err = do_ipv6_getsockopt(sk, level, optname,
- USER_SOCKPTR(optval), USER_SOCKPTR(optlen));
+ err = sockopt_init_user(&sopt, optval, optlen);
+ if (err)
+ return err;
+
+ err = do_ipv6_getsockopt(sk, level, optname, &sopt);
+ if (put_user(sopt.optlen, optlen))
+ return -EFAULT;
#ifdef CONFIG_NETFILTER
/* we need to exclude all possible ENOPROTOOPTs except default case */
if (err == -ENOPROTOOPT && optname != IPV6_2292PKTOPTIONS) {
- int len;
-
- if (get_user(len, optlen))
- return -EFAULT;
+ int len = sopt.optlen;

err = nf_getsockopt(sk, PF_INET6, optname, optval, &len);
if (err >= 0)

--
2.53.0-Meta