tree 51c4480c9508a911d52a3f69bbe84ec1191fd202
parent fd92833a52b972aafacced959f4a3f7541936a9b
author Herbert Xu <[EMAIL PROTECTED]> Wed, 20 Apr 2005 12:30:14 -0700
committer David S. Miller <[EMAIL PROTECTED]> Wed, 20 Apr 2005 12:30:14 -0700

[IPV6]: IPV6_CHECKSUM socket option can corrupt kernel memory

So here is a patch that introduces skb_store_bits -- the opposite of
skb_copy_bits, and uses them to read/write the csum field in rawv6.

Signed-off-by: Herbert Xu <[EMAIL PROTECTED]>
Signed-off-by: David S. Miller <[EMAIL PROTECTED]>

 linux/skbuff.h |    2 +
 core/skbuff.c  |   88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 ipv6/raw.c     |   53 +++++++++++++++++++++++++---------
 3 files changed, 130 insertions(+), 13 deletions(-)

Index: include/linux/skbuff.h
===================================================================
--- 78d8e11eca4d84b91f5be4a28ad81ba523f9dc65/include/linux/skbuff.h  
(mode:100644 sha1:aa35797ebfbf773fae3203094d6c6628ed7bcf35)
+++ 51c4480c9508a911d52a3f69bbe84ec1191fd202/include/linux/skbuff.h  
(mode:100644 sha1:9f2d75e4f087642d2af265e0b477d0db53ee56f1)
@@ -1183,6 +1183,8 @@
                                    int len, unsigned int csum);
 extern int            skb_copy_bits(const struct sk_buff *skb, int offset,
                                     void *to, int len);
+extern int            skb_store_bits(const struct sk_buff *skb, int offset,
+                                     void *from, int len);
 extern unsigned int    skb_copy_and_csum_bits(const struct sk_buff *skb,
                                              int offset, u8 *to, int len,
                                              unsigned int csum);
Index: net/core/skbuff.c
===================================================================
--- 78d8e11eca4d84b91f5be4a28ad81ba523f9dc65/net/core/skbuff.c  (mode:100644 
sha1:bf02ca9f80ac15bfaddf9ef0624e94c5623802e0)
+++ 51c4480c9508a911d52a3f69bbe84ec1191fd202/net/core/skbuff.c  (mode:100644 
sha1:c96559574a3fc281d0642846eb1bc49206eabcb4)
@@ -985,6 +985,94 @@
        return -EFAULT;
 }
 
+/**
+ *     skb_store_bits - store bits from kernel buffer to skb
+ *     @skb: destination buffer
+ *     @offset: offset in destination
+ *     @from: source buffer
+ *     @len: number of bytes to copy
+ *
+ *     Copy the specified number of bytes from the source buffer to the
+ *     destination skb.  This function handles all the messy bits of
+ *     traversing fragment lists and such.
+ */
+
+int skb_store_bits(const struct sk_buff *skb, int offset, void *from, int len)
+{
+       int i, copy;
+       int start = skb_headlen(skb);
+
+       if (offset > (int)skb->len - len)
+               goto fault;
+
+       if ((copy = start - offset) > 0) {
+               if (copy > len)
+                       copy = len;
+               memcpy(skb->data + offset, from, copy);
+               if ((len -= copy) == 0)
+                       return 0;
+               offset += copy;
+               from += copy;
+       }
+
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+               int end;
+
+               BUG_TRAP(start <= offset + len);
+
+               end = start + frag->size;
+               if ((copy = end - offset) > 0) {
+                       u8 *vaddr;
+
+                       if (copy > len)
+                               copy = len;
+
+                       vaddr = kmap_skb_frag(frag);
+                       memcpy(vaddr + frag->page_offset + offset - start,
+                              from, copy);
+                       kunmap_skb_frag(vaddr);
+
+                       if ((len -= copy) == 0)
+                               return 0;
+                       offset += copy;
+                       from += copy;
+               }
+               start = end;
+       }
+
+       if (skb_shinfo(skb)->frag_list) {
+               struct sk_buff *list = skb_shinfo(skb)->frag_list;
+
+               for (; list; list = list->next) {
+                       int end;
+
+                       BUG_TRAP(start <= offset + len);
+
+                       end = start + list->len;
+                       if ((copy = end - offset) > 0) {
+                               if (copy > len)
+                                       copy = len;
+                               if (skb_store_bits(list, offset - start,
+                                                  from, copy))
+                                       goto fault;
+                               if ((len -= copy) == 0)
+                                       return 0;
+                               offset += copy;
+                               from += copy;
+                       }
+                       start = end;
+               }
+       }
+       if (!len)
+               return 0;
+
+fault:
+       return -EFAULT;
+}
+
+EXPORT_SYMBOL(skb_store_bits);
+
 /* Checksum skb data. */
 
 unsigned int skb_checksum(const struct sk_buff *skb, int offset,
Index: net/ipv6/raw.c
===================================================================
--- 78d8e11eca4d84b91f5be4a28ad81ba523f9dc65/net/ipv6/raw.c  (mode:100644 
sha1:5488ad0de4f6bd227665657fc65b7fce4fafcca4)
+++ 51c4480c9508a911d52a3f69bbe84ec1191fd202/net/ipv6/raw.c  (mode:100644 
sha1:3e2ad0a7041274f7e6b8aab83685c414d195ab31)
@@ -34,6 +34,7 @@
 #include <linux/netfilter_ipv6.h>
 #include <asm/uaccess.h>
 #include <asm/ioctls.h>
+#include <asm/bug.h>
 
 #include <net/ip.h>
 #include <net/sock.h>
@@ -452,12 +453,15 @@
 }
 
 static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl,
-                                    struct raw6_sock *rp, int len)
+                                    struct raw6_sock *rp)
 {
+       struct inet_sock *inet = inet_sk(sk);
        struct sk_buff *skb;
        int err = 0;
-       u16 *csum;
+       int offset;
+       int len;
        u32 tmp_csum;
+       u16 csum;
 
        if (!rp->checksum)
                goto send;
@@ -465,10 +469,10 @@
        if ((skb = skb_peek(&sk->sk_write_queue)) == NULL)
                goto out;
 
-       if (rp->offset + 1 < len)
-               csum = (u16 *)(skb->h.raw + rp->offset);
-       else {
+       offset = rp->offset;
+       if (offset >= inet->cork.length - 1) {
                err = -EINVAL;
+               ip6_flush_pending_frames(sk);
                goto out;
        }
 
@@ -479,23 +483,46 @@
                 */
                tmp_csum = skb->csum;
        } else {
+               struct sk_buff *csum_skb = NULL;
                tmp_csum = 0;
 
                skb_queue_walk(&sk->sk_write_queue, skb) {
                        tmp_csum = csum_add(tmp_csum, skb->csum);
+
+                       if (csum_skb)
+                               continue;
+
+                       len = skb->len - (skb->h.raw - skb->data);
+                       if (offset >= len) {
+                               offset -= len;
+                               continue;
+                       }
+
+                       csum_skb = skb;
                }
+
+               skb = csum_skb;
        }
 
+       offset += skb->h.raw - skb->data;
+       if (skb_copy_bits(skb, offset, &csum, 2))
+               BUG();
+
        /* in case cksum was not initialized */
-       if (unlikely(*csum))
-               tmp_csum = csum_sub(tmp_csum, *csum);
+       if (unlikely(csum))
+               tmp_csum = csum_sub(tmp_csum, csum);
 
-       *csum = csum_ipv6_magic(&fl->fl6_src,
-                               &fl->fl6_dst,
-                               len, fl->proto, tmp_csum);
+       tmp_csum = csum_ipv6_magic(&fl->fl6_src,
+                                  &fl->fl6_dst,
+                                  inet->cork.length, fl->proto, tmp_csum);
+
+       if (tmp_csum == 0)
+               tmp_csum = -1;
+
+       csum = tmp_csum;
+       if (skb_store_bits(skb, offset, &csum, 2))
+               BUG();
 
-       if (*csum == 0)
-               *csum = -1;
 send:
        err = ip6_push_pending_frames(sk);
 out:
@@ -774,7 +801,7 @@
                if (err)
                        ip6_flush_pending_frames(sk);
                else if (!(msg->msg_flags & MSG_MORE))
-                       err = rawv6_push_pending_frames(sk, &fl, rp, len);
+                       err = rawv6_push_pending_frames(sk, &fl, rp);
        }
 done:
        ip6_dst_store(sk, dst,
-
To unsubscribe from this list: send the line "unsubscribe bk-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to