]> git.hungrycats.org Git - linux/commitdiff
net: add length argument to skb_copy_and_csum_datagram_iovec
authorSabrina Dubroca <sd@queasysnail.net>
Thu, 15 Oct 2015 12:25:03 +0000 (14:25 +0200)
committerWilly Tarreau <w@1wt.eu>
Sat, 5 Dec 2015 23:49:13 +0000 (00:49 +0100)
Without this length argument, we can read past the end of the iovec in
memcpy_toiovec because we have no way of knowing the total length of the
iovec's buffers.

This is needed for stable kernels where 89c22d8c3b27 ("net: Fix skb
csum races when peeking") has been backported but that don't have the
ioviter conversion, which is almost all the stable trees <= 3.18.

This also fixes a kernel crash for NFS servers when the client uses
 -onfsvers=3,proto=udp to mount the export.

Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
Reviewed-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
[bwh: Backported to 3.2: adjust context in include/linux/skbuff.h]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
(cherry picked from commit 127500d724f8c43f452610c9080444eedb5eaa6c)

Signed-off-by: Willy Tarreau <w@1wt.eu>
include/linux/skbuff.h
net/core/datagram.c
net/ipv4/tcp_input.c
net/ipv4/udp.c
net/ipv6/raw.c
net/ipv6/udp.c
net/rxrpc/ar-recvmsg.c

index ae77862160ba4eeb3b9f1bc96b1db6d61700283c..c282a2cba635f8330a915ae2ece2f44bc26afcc3 100644 (file)
@@ -1765,7 +1765,8 @@ extern int               skb_copy_datagram_iovec(const struct sk_buff *from,
                                               int size);
 extern int            skb_copy_and_csum_datagram_iovec(struct sk_buff *skb,
                                                        int hlen,
-                                                       struct iovec *iov);
+                                                       struct iovec *iov,
+                                                       int len);
 extern int            skb_copy_datagram_from_iovec(struct sk_buff *skb,
                                                    int offset,
                                                    const struct iovec *from,
index 253d068e8cbac54be240f5d603b9466e3a6c3b84..767c17aaddcb766715d376a99e9829d1ad68cf13 100644 (file)
@@ -693,6 +693,7 @@ EXPORT_SYMBOL(__skb_checksum_complete);
  *     @skb: skbuff
  *     @hlen: hardware length
  *     @iov: io vector
+ *     @len: amount of data to copy from skb to iov
  *
  *     Caller _must_ check that skb will fit to this iovec.
  *
@@ -702,11 +703,14 @@ EXPORT_SYMBOL(__skb_checksum_complete);
  *                        can be modified!
  */
 int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb,
-                                    int hlen, struct iovec *iov)
+                                    int hlen, struct iovec *iov, int len)
 {
        __wsum csum;
        int chunk = skb->len - hlen;
 
+       if (chunk > len)
+               chunk = len;
+
        if (!chunk)
                return 0;
 
index c821218b3834caa04c664ed0b79c9d3e1a6527eb..d3dcfb9d736f5348f3c03baf74dfa9cb0d4b5c49 100644 (file)
@@ -4985,7 +4985,7 @@ static int tcp_copy_to_iovec(struct sock *sk, struct sk_buff *skb, int hlen)
                err = skb_copy_datagram_iovec(skb, hlen, tp->ucopy.iov, chunk);
        else
                err = skb_copy_and_csum_datagram_iovec(skb, hlen,
-                                                      tp->ucopy.iov);
+                                                      tp->ucopy.iov, chunk);
 
        if (!err) {
                tp->ucopy.len -= chunk;
index 3ae286b9c5ab1ffd5e09f00b075401237126b032..83b507d51968f5a0482b3d15c1682d151e0bbb6e 100644 (file)
@@ -975,7 +975,7 @@ try_again:
        else {
                err = skb_copy_and_csum_datagram_iovec(skb,
                                                       sizeof(struct udphdr),
-                                                      msg->msg_iov);
+                                                      msg->msg_iov, copied);
 
                if (err == -EINVAL)
                        goto csum_copy_err;
index d5b09c7ca4020ff4109eb3412c7170ad90c6096b..f016542cf8496dca402b5a55a274f042b8f22a18 100644 (file)
@@ -476,7 +476,7 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk,
                        goto csum_copy_err;
                err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
        } else {
-               err = skb_copy_and_csum_datagram_iovec(skb, 0, msg->msg_iov);
+               err = skb_copy_and_csum_datagram_iovec(skb, 0, msg->msg_iov, copied);
                if (err == -EINVAL)
                        goto csum_copy_err;
        }
index 0b023f35103dc6affa95ec34ba44daf0e3ee9c02..5c8bd19c9572dfedbb554ce44c30c4aa27a74829 100644 (file)
@@ -233,7 +233,8 @@ try_again:
                err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr),
                                              msg->msg_iov, copied       );
        else {
-               err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov);
+               err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr),
+                                                      msg->msg_iov, copied);
                if (err == -EINVAL)
                        goto csum_copy_err;
        }
index b6076b231b492243f28f8a08741d27b6c19ffa65..813e1c482e32a00b8ae61e2973ec2b73f6517947 100644 (file)
@@ -184,7 +184,8 @@ int rxrpc_recvmsg(struct kiocb *iocb, struct socket *sock,
                                                      msg->msg_iov, copy);
                } else {
                        ret = skb_copy_and_csum_datagram_iovec(skb, offset,
-                                                              msg->msg_iov);
+                                                              msg->msg_iov,
+                                                              copy);
                        if (ret == -EINVAL)
                                goto csum_copy_error;
                }