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 <[email protected]> --- 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

