summaryrefslogtreecommitdiffstats
path: root/net/core/skbuff.c
diff options
context:
space:
mode:
authorHerbert Xu2005-04-20 07:30:14 +0200
committerDavid S. Miller2005-04-20 07:30:14 +0200
commit357b40a18b04c699da1d45608436e9b76b50e251 (patch)
tree51c4480c9508a911d52a3f69bbe84ec1191fd202 /net/core/skbuff.c
parent[IPV6]: Fix a branch prediction (diff)
downloadkernel-qcow2-linux-357b40a18b04c699da1d45608436e9b76b50e251.tar.gz
kernel-qcow2-linux-357b40a18b04c699da1d45608436e9b76b50e251.tar.xz
kernel-qcow2-linux-357b40a18b04c699da1d45608436e9b76b50e251.zip
[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 <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core/skbuff.c')
-rw-r--r--net/core/skbuff.c88
1 files changed, 88 insertions, 0 deletions
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index bf02ca9f80ac..c96559574a3f 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -985,6 +985,94 @@ fault:
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,