mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	[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>
This commit is contained in:
		
							parent
							
								
									fd92833a52
								
							
						
					
					
						commit
						357b40a18b
					
				
					 3 changed files with 130 additions and 13 deletions
				
			
		|  | @ -1183,6 +1183,8 @@ extern unsigned int    skb_checksum(const struct sk_buff *skb, int offset, | |||
| 				    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); | ||||
|  |  | |||
|  | @ -985,6 +985,94 @@ int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len) | |||
| 	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, | ||||
|  |  | |||
|  | @ -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_recvmsg(struct kiocb *iocb, struct sock *sk, | |||
| } | ||||
| 
 | ||||
| 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 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl, | |||
| 	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 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl, | |||
| 		 */ | ||||
| 		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 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, | |||
| 		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, | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Herbert Xu
						Herbert Xu