diff options
Diffstat (limited to 'net/ipv4/udp.c')
| -rw-r--r-- | net/ipv4/udp.c | 52 |
1 files changed, 37 insertions, 15 deletions
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 70f6cbd4ef73..af9603217444 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -76,6 +76,7 @@ #include <linux/bpf-cgroup.h> #include <linux/uaccess.h> +#include <linux/uio.h> #include <asm/ioctls.h> #include <linux/memblock.h> #include <linux/highmem.h> @@ -1107,7 +1108,8 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4, uh = udp_hdr(skb); uh->source = inet_sk(sk)->inet_sport; uh->dest = fl4->fl4_dport; - uh->len = htons(len); + /* Datagram length checked in udp_sendmsg. */ + udp_set_len_short(uh, len); uh->check = 0; if (cork->gso_size) { @@ -2475,6 +2477,7 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb, struct udp_hslot *hslot; struct sk_buff *nskb; bool use_hash2; + int ret; hash2_any = 0; hash2 = 0; @@ -2519,8 +2522,9 @@ start_lookup: } if (first) { - if (udp_queue_rcv_skb(first, skb) > 0) - consume_skb(skb); + ret = udp_queue_rcv_skb(first, skb); + if (ret > 0) + return -ret; } else { kfree_skb(skb); __UDP_INC_STATS(net, UDP_MIB_IGNOREDMULTI); @@ -2590,8 +2594,8 @@ int udp_rcv(struct sk_buff *skb) struct rtable *rt = skb_rtable(skb); struct net *net = dev_net(skb->dev); struct sock *sk = NULL; - unsigned short ulen; __be32 saddr, daddr; + unsigned int ulen; struct udphdr *uh; bool refcounted; int drop_reason; @@ -2605,7 +2609,7 @@ int udp_rcv(struct sk_buff *skb) goto drop; /* No space for header. */ uh = udp_hdr(skb); - ulen = ntohs(uh->len); + ulen = udp_get_len(skb, uh, 0); saddr = ip_hdr(skb)->saddr; daddr = ip_hdr(skb)->daddr; @@ -2995,14 +2999,13 @@ static int udp_setsockopt(struct sock *sk, int level, int optname, sockptr_t opt } int udp_lib_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct udp_sock *up = udp_sk(sk); int val, len; - if (get_user(len, optlen)) - return -EFAULT; - + len = opt->optlen; + /* keep the check so direct sockopt_t callers stay covered. */ if (len < 0) return -EINVAL; @@ -3037,9 +3040,8 @@ int udp_lib_getsockopt(struct sock *sk, 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; } @@ -3047,9 +3049,29 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname, static int udp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { - if (level == SOL_UDP) - return udp_lib_getsockopt(sk, level, optname, optval, optlen); - return ip_getsockopt(sk, level, optname, optval, optlen); + sockopt_t opt; + int err; + + /* + * keep the old __user pointers, until ip_getsockopt() moves + * to sockopt_t + */ + if (level != SOL_UDP) + return ip_getsockopt(sk, level, optname, optval, optlen); + + err = sockopt_init_user(&opt, optval, optlen); + if (err) + return err; + + err = udp_lib_getsockopt(sk, level, optname, &opt); + if (err) + return err; + + /* optval was written by copy_to_iter() in udp_lib_getsockopt() */ + if (put_user(opt.optlen, optlen)) + return -EFAULT; + + return 0; } /** |
