summaryrefslogtreecommitdiff
path: root/net/ipv4/udp.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/udp.c')
-rw-r--r--net/ipv4/udp.c52
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;
}
/**