diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2005-04-20 01:30:14 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2005-04-20 01:30:14 -0400 |
commit | 357b40a18b04c699da1d45608436e9b76b50e251 (patch) | |
tree | 51c4480c9508a911d52a3f69bbe84ec1191fd202 /net/core | |
parent | fd92833a52b972aafacced959f4a3f7541936a9b (diff) |
[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')
-rw-r--r-- | net/core/skbuff.c | 88 |
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: | |||
985 | return -EFAULT; | 985 | return -EFAULT; |
986 | } | 986 | } |
987 | 987 | ||
988 | /** | ||
989 | * skb_store_bits - store bits from kernel buffer to skb | ||
990 | * @skb: destination buffer | ||
991 | * @offset: offset in destination | ||
992 | * @from: source buffer | ||
993 | * @len: number of bytes to copy | ||
994 | * | ||
995 | * Copy the specified number of bytes from the source buffer to the | ||
996 | * destination skb. This function handles all the messy bits of | ||
997 | * traversing fragment lists and such. | ||
998 | */ | ||
999 | |||
1000 | int skb_store_bits(const struct sk_buff *skb, int offset, void *from, int len) | ||
1001 | { | ||
1002 | int i, copy; | ||
1003 | int start = skb_headlen(skb); | ||
1004 | |||
1005 | if (offset > (int)skb->len - len) | ||
1006 | goto fault; | ||
1007 | |||
1008 | if ((copy = start - offset) > 0) { | ||
1009 | if (copy > len) | ||
1010 | copy = len; | ||
1011 | memcpy(skb->data + offset, from, copy); | ||
1012 | if ((len -= copy) == 0) | ||
1013 | return 0; | ||
1014 | offset += copy; | ||
1015 | from += copy; | ||
1016 | } | ||
1017 | |||
1018 | for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { | ||
1019 | skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; | ||
1020 | int end; | ||
1021 | |||
1022 | BUG_TRAP(start <= offset + len); | ||
1023 | |||
1024 | end = start + frag->size; | ||
1025 | if ((copy = end - offset) > 0) { | ||
1026 | u8 *vaddr; | ||
1027 | |||
1028 | if (copy > len) | ||
1029 | copy = len; | ||
1030 | |||
1031 | vaddr = kmap_skb_frag(frag); | ||
1032 | memcpy(vaddr + frag->page_offset + offset - start, | ||
1033 | from, copy); | ||
1034 | kunmap_skb_frag(vaddr); | ||
1035 | |||
1036 | if ((len -= copy) == 0) | ||
1037 | return 0; | ||
1038 | offset += copy; | ||
1039 | from += copy; | ||
1040 | } | ||
1041 | start = end; | ||
1042 | } | ||
1043 | |||
1044 | if (skb_shinfo(skb)->frag_list) { | ||
1045 | struct sk_buff *list = skb_shinfo(skb)->frag_list; | ||
1046 | |||
1047 | for (; list; list = list->next) { | ||
1048 | int end; | ||
1049 | |||
1050 | BUG_TRAP(start <= offset + len); | ||
1051 | |||
1052 | end = start + list->len; | ||
1053 | if ((copy = end - offset) > 0) { | ||
1054 | if (copy > len) | ||
1055 | copy = len; | ||
1056 | if (skb_store_bits(list, offset - start, | ||
1057 | from, copy)) | ||
1058 | goto fault; | ||
1059 | if ((len -= copy) == 0) | ||
1060 | return 0; | ||
1061 | offset += copy; | ||
1062 | from += copy; | ||
1063 | } | ||
1064 | start = end; | ||
1065 | } | ||
1066 | } | ||
1067 | if (!len) | ||
1068 | return 0; | ||
1069 | |||
1070 | fault: | ||
1071 | return -EFAULT; | ||
1072 | } | ||
1073 | |||
1074 | EXPORT_SYMBOL(skb_store_bits); | ||
1075 | |||
988 | /* Checksum skb data. */ | 1076 | /* Checksum skb data. */ |
989 | 1077 | ||
990 | unsigned int skb_checksum(const struct sk_buff *skb, int offset, | 1078 | unsigned int skb_checksum(const struct sk_buff *skb, int offset, |