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)
committerBen Hutchings <ben@decadent.org.uk>
Tue, 17 Nov 2015 15:54:45 +0000 (15:54 +0000)
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>
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 1b4ea29..d38c2ee 100644 (file)
@@ -2131,7 +2131,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,
                                               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,
 extern int            skb_copy_datagram_from_iovec(struct sk_buff *skb,
                                                    int offset,
                                                    const struct iovec *from,
index a496313..6616bfb 100644 (file)
@@ -709,6 +709,7 @@ EXPORT_SYMBOL(__skb_checksum_complete);
  *     @skb: skbuff
  *     @hlen: hardware length
  *     @iov: io vector
  *     @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.
  *
  *
  *     Caller _must_ check that skb will fit to this iovec.
  *
@@ -718,11 +719,14 @@ EXPORT_SYMBOL(__skb_checksum_complete);
  *                        can be modified!
  */
 int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb,
  *                        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;
 
 {
        __wsum csum;
        int chunk = skb->len - hlen;
 
+       if (chunk > len)
+               chunk = len;
+
        if (!chunk)
                return 0;
 
        if (!chunk)
                return 0;
 
index afe6886..f8b5899 100644 (file)
@@ -5198,7 +5198,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,
                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;
 
        if (!err) {
                tp->ucopy.len -= chunk;
index 5e92043..b111221 100644 (file)
@@ -1207,7 +1207,7 @@ try_again:
        else {
                err = skb_copy_and_csum_datagram_iovec(skb,
                                                       sizeof(struct udphdr),
        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;
 
                if (err == -EINVAL)
                        goto csum_copy_err;
index 9287f3e..240c102 100644 (file)
@@ -479,7 +479,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 {
                        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;
        }
                if (err == -EINVAL)
                        goto csum_copy_err;
        }
index dc08afd..888892e 100644 (file)
@@ -383,7 +383,8 @@ try_again:
                err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr),
                                              msg->msg_iov, copied       );
        else {
                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;
        }
                if (err == -EINVAL)
                        goto csum_copy_err;
        }
index 5cc2da5..c67f5d3 100644 (file)
@@ -185,7 +185,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, 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;
                }
                        if (ret == -EINVAL)
                                goto csum_copy_error;
                }