Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

[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>

authored by

Herbert Xu and committed by
David S. Miller
357b40a1 fd92833a

+130 -13
+2
include/linux/skbuff.h
··· 1183 1183 int len, unsigned int csum); 1184 1184 extern int skb_copy_bits(const struct sk_buff *skb, int offset, 1185 1185 void *to, int len); 1186 + extern int skb_store_bits(const struct sk_buff *skb, int offset, 1187 + void *from, int len); 1186 1188 extern unsigned int skb_copy_and_csum_bits(const struct sk_buff *skb, 1187 1189 int offset, u8 *to, int len, 1188 1190 unsigned int csum);
+88
net/core/skbuff.c
··· 985 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 1076 /* Checksum skb data. */ 989 1077 990 1078 unsigned int skb_checksum(const struct sk_buff *skb, int offset,
+40 -13
net/ipv6/raw.c
··· 34 34 #include <linux/netfilter_ipv6.h> 35 35 #include <asm/uaccess.h> 36 36 #include <asm/ioctls.h> 37 + #include <asm/bug.h> 37 38 38 39 #include <net/ip.h> 39 40 #include <net/sock.h> ··· 453 452 } 454 453 455 454 static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl, 456 - struct raw6_sock *rp, int len) 455 + struct raw6_sock *rp) 457 456 { 457 + struct inet_sock *inet = inet_sk(sk); 458 458 struct sk_buff *skb; 459 459 int err = 0; 460 - u16 *csum; 460 + int offset; 461 + int len; 461 462 u32 tmp_csum; 463 + u16 csum; 462 464 463 465 if (!rp->checksum) 464 466 goto send; ··· 469 465 if ((skb = skb_peek(&sk->sk_write_queue)) == NULL) 470 466 goto out; 471 467 472 - if (rp->offset + 1 < len) 473 - csum = (u16 *)(skb->h.raw + rp->offset); 474 - else { 468 + offset = rp->offset; 469 + if (offset >= inet->cork.length - 1) { 475 470 err = -EINVAL; 471 + ip6_flush_pending_frames(sk); 476 472 goto out; 477 473 } 478 474 ··· 483 479 */ 484 480 tmp_csum = skb->csum; 485 481 } else { 482 + struct sk_buff *csum_skb = NULL; 486 483 tmp_csum = 0; 487 484 488 485 skb_queue_walk(&sk->sk_write_queue, skb) { 489 486 tmp_csum = csum_add(tmp_csum, skb->csum); 487 + 488 + if (csum_skb) 489 + continue; 490 + 491 + len = skb->len - (skb->h.raw - skb->data); 492 + if (offset >= len) { 493 + offset -= len; 494 + continue; 495 + } 496 + 497 + csum_skb = skb; 490 498 } 499 + 500 + skb = csum_skb; 491 501 } 492 502 503 + offset += skb->h.raw - skb->data; 504 + if (skb_copy_bits(skb, offset, &csum, 2)) 505 + BUG(); 506 + 493 507 /* in case cksum was not initialized */ 494 - if (unlikely(*csum)) 495 - tmp_csum = csum_sub(tmp_csum, *csum); 508 + if (unlikely(csum)) 509 + tmp_csum = csum_sub(tmp_csum, csum); 496 510 497 - *csum = csum_ipv6_magic(&fl->fl6_src, 498 - &fl->fl6_dst, 499 - len, fl->proto, tmp_csum); 511 + tmp_csum = csum_ipv6_magic(&fl->fl6_src, 512 + &fl->fl6_dst, 513 + inet->cork.length, fl->proto, tmp_csum); 500 514 501 - if (*csum == 0) 502 - *csum = -1; 515 + if (tmp_csum == 0) 516 + tmp_csum = -1; 517 + 518 + csum = tmp_csum; 519 + if (skb_store_bits(skb, offset, &csum, 2)) 520 + BUG(); 521 + 503 522 send: 504 523 err = ip6_push_pending_frames(sk); 505 524 out: ··· 801 774 if (err) 802 775 ip6_flush_pending_frames(sk); 803 776 else if (!(msg->msg_flags & MSG_MORE)) 804 - err = rawv6_push_pending_frames(sk, &fl, rp, len); 777 + err = rawv6_push_pending_frames(sk, &fl, rp); 805 778 } 806 779 done: 807 780 ip6_dst_store(sk, dst,