[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <1482323232.2260.2.camel@stressinduktion.org>
Date: Wed, 21 Dec 2016 13:27:12 +0100
From: Hannes Frederic Sowa <hannes@...essinduktion.org>
To: Cong Wang <xiyou.wangcong@...il.com>,
Dave Jones <davej@...emonkey.org.uk>
Cc: David Miller <davem@...emloft.net>,
Linux Kernel Network Developers <netdev@...r.kernel.org>
Subject: Re: ipv6: handle -EFAULT from skb_copy_bits
On Tue, 2016-12-20 at 22:09 -0800, Cong Wang wrote:
> On Tue, Dec 20, 2016 at 2:12 PM, Dave Jones <davej@...emonkey.org.uk> wrote:
> > fd = socket(AF_INET6, SOCK_RAW, 7);
> >
> > setsockopt(fd, SOL_IPV6, IPV6_CHECKSUM, &zero, 4);
> > setsockopt(fd, SOL_IPV6, IPV6_DSTOPTS, &buf, LEN);
> >
>
> Interesting, you set the checksum offset to be 0, but the packet size
> is actually 49, transport header is located at offset 48, so apparently
> the packet doesn't have room for a 16bit checksum after network header.
>
> Your original patch seems reasonable to me, unless there is some
> check in __ip6_append_data() which is supposed to catch this, but
> CHECKSUM is specific to raw socket only.
The calculation of total_len is wrong here:
total_len = inet_sk(sk)->cork.base.length;
if (offset >= total_len - 1) {
err = -EINVAL;
ip6_flush_pending_frames(sk);
goto out;
}
At least for this bug to fix we need to subtract the extension header
length after the fragmentation header, so:
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -536,6 +536,17 @@ static int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
goto out;
}
+static unsigned int raw6_corked_transport_len(const struct sock *sk)
+{
+ unsigned int len = inet_sk(sk)->cork.base.length;
+ struct ipv6_txoptions *opt = inet6_sk(sk)->cork.opt;
+
+ if (likely(!opt))
+ return len;
+
+ return len - opt->opt_flen;
+}
+
static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
struct raw6_sock *rp)
{
@@ -543,7 +554,7 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
int err = 0;
int offset;
int len;
- int total_len;
+ int transport_len;
__wsum tmp_csum;
__sum16 csum;
@@ -555,8 +566,8 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
goto out;
offset = rp->offset;
- total_len = inet_sk(sk)->cork.base.length;
- if (offset >= total_len - 1) {
+ transport_len = raw6_corked_transport_len(sk);
+ if (offset >= transport_len - 1) {
err = -EINVAL;
ip6_flush_pending_frames(sk);
goto out;
@@ -598,7 +609,7 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
tmp_csum = csum_sub(tmp_csum, csum_unfold(csum));
csum = csum_ipv6_magic(&fl6->saddr, &fl6->daddr,
- total_len, fl6->flowi6_proto, tmp_csum);
+ transport_len, fl6->flowi6_proto, tmp_csum);
if (csum == 0 && fl6->flowi6_proto == IPPROTO_UDP)
csum = CSUM_MANGLED_0;
Powered by blists - more mailing lists