[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:
Родитель
fd92833a52
Коммит
357b40a18b
|
@ -1183,6 +1183,8 @@ extern unsigned int skb_checksum(const struct sk_buff *skb, int offset,
|
||||||
int len, unsigned int csum);
|
int len, unsigned int csum);
|
||||||
extern int skb_copy_bits(const struct sk_buff *skb, int offset,
|
extern int skb_copy_bits(const struct sk_buff *skb, int offset,
|
||||||
void *to, int len);
|
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,
|
extern unsigned int skb_copy_and_csum_bits(const struct sk_buff *skb,
|
||||||
int offset, u8 *to, int len,
|
int offset, u8 *to, int len,
|
||||||
unsigned int csum);
|
unsigned int csum);
|
||||||
|
|
|
@ -985,6 +985,94 @@ fault:
|
||||||
return -EFAULT;
|
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. */
|
/* Checksum skb data. */
|
||||||
|
|
||||||
unsigned int skb_checksum(const struct sk_buff *skb, int offset,
|
unsigned int skb_checksum(const struct sk_buff *skb, int offset,
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include <linux/netfilter_ipv6.h>
|
#include <linux/netfilter_ipv6.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/ioctls.h>
|
#include <asm/ioctls.h>
|
||||||
|
#include <asm/bug.h>
|
||||||
|
|
||||||
#include <net/ip.h>
|
#include <net/ip.h>
|
||||||
#include <net/sock.h>
|
#include <net/sock.h>
|
||||||
|
@ -452,12 +453,15 @@ csum_copy_err:
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl,
|
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;
|
struct sk_buff *skb;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
u16 *csum;
|
int offset;
|
||||||
|
int len;
|
||||||
u32 tmp_csum;
|
u32 tmp_csum;
|
||||||
|
u16 csum;
|
||||||
|
|
||||||
if (!rp->checksum)
|
if (!rp->checksum)
|
||||||
goto send;
|
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)
|
if ((skb = skb_peek(&sk->sk_write_queue)) == NULL)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (rp->offset + 1 < len)
|
offset = rp->offset;
|
||||||
csum = (u16 *)(skb->h.raw + rp->offset);
|
if (offset >= inet->cork.length - 1) {
|
||||||
else {
|
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
|
ip6_flush_pending_frames(sk);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,23 +483,46 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl,
|
||||||
*/
|
*/
|
||||||
tmp_csum = skb->csum;
|
tmp_csum = skb->csum;
|
||||||
} else {
|
} else {
|
||||||
|
struct sk_buff *csum_skb = NULL;
|
||||||
tmp_csum = 0;
|
tmp_csum = 0;
|
||||||
|
|
||||||
skb_queue_walk(&sk->sk_write_queue, skb) {
|
skb_queue_walk(&sk->sk_write_queue, skb) {
|
||||||
tmp_csum = csum_add(tmp_csum, skb->csum);
|
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 */
|
/* in case cksum was not initialized */
|
||||||
if (unlikely(*csum))
|
if (unlikely(csum))
|
||||||
tmp_csum = csum_sub(tmp_csum, *csum);
|
tmp_csum = csum_sub(tmp_csum, csum);
|
||||||
|
|
||||||
*csum = csum_ipv6_magic(&fl->fl6_src,
|
tmp_csum = csum_ipv6_magic(&fl->fl6_src,
|
||||||
&fl->fl6_dst,
|
&fl->fl6_dst,
|
||||||
len, fl->proto, tmp_csum);
|
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:
|
send:
|
||||||
err = ip6_push_pending_frames(sk);
|
err = ip6_push_pending_frames(sk);
|
||||||
out:
|
out:
|
||||||
|
@ -774,7 +801,7 @@ back_from_confirm:
|
||||||
if (err)
|
if (err)
|
||||||
ip6_flush_pending_frames(sk);
|
ip6_flush_pending_frames(sk);
|
||||||
else if (!(msg->msg_flags & MSG_MORE))
|
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:
|
done:
|
||||||
ip6_dst_store(sk, dst,
|
ip6_dst_store(sk, dst,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче