diff mbox series

[v2] net: ipv6: fix skb_over_panic in __ip6_append_data

Message ID 20220310221328.877987-1-tadeusz.struk@linaro.org (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series [v2] net: ipv6: fix skb_over_panic in __ip6_append_data | expand

Checks

Context Check Description
netdev/tree_selection success Guessed tree name to be net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix warning Target tree name not specified in the subject
netdev/cover_letter success Single patches do not need cover letters
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers success CCed 14 of 14 maintainers
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch warning WARNING: networking block comments don't use an empty /* line, use /* Comment...
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Tadeusz Struk March 10, 2022, 10:13 p.m. UTC
Syzbot found a kernel bug in the ipv6 stack:
LINK: https://syzkaller.appspot.com/bug?id=205d6f11d72329ab8d62a610c44c5e7e25415580
The reproducer triggers it by sending a crafted message via sendmmsg()
call, which triggers skb_over_panic, and crashes the kernel:

skbuff: skb_over_panic: text:ffffffff84647fb4 len:65575 put:65575
head:ffff888109ff0000 data:ffff888109ff0088 tail:0x100af end:0xfec0
dev:<NULL>

Add a check that prevents an invalid packet with MTU equall to the
fregment header size to eat up all the space for payload.

The reproducer can be found here:
LINK: https://syzkaller.appspot.com/text?tag=ReproC&x=1648c83fb00000

Cc: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
Cc: David Ahern <dsahern@kernel.org>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Andrii Nakryiko <andrii@kernel.org>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: Song Liu <songliubraving@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: John Fastabend <john.fastabend@gmail.com>
Cc: KP Singh <kpsingh@kernel.org>
Cc: netdev@vger.kernel.org
Cc: bpf@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org

Reported-by: syzbot+e223cf47ec8ae183f2a0@syzkaller.appspotmail.com
Signed-off-by: Tadeusz Struk <tadeusz.struk@linaro.org>
---
v2: Instead of updating the alloclen add a check that prevents
    an invalid packet with MTU equall to the fregment header size
    to eat up all the space for payload.
    Fix suggested by Willem de Bruijn <willemdebruijn.kernel@gmail.com>
---
 net/ipv6/ip6_output.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

Comments

David Laight March 10, 2022, 10:18 p.m. UTC | #1
From: Tadeusz Struk
> Sent: 10 March 2022 22:13
> 
> Syzbot found a kernel bug in the ipv6 stack:
> LINK: https://syzkaller.appspot.com/bug?id=205d6f11d72329ab8d62a610c44c5e7e25415580
> The reproducer triggers it by sending a crafted message via sendmmsg()
> call, which triggers skb_over_panic, and crashes the kernel:
> 
> skbuff: skb_over_panic: text:ffffffff84647fb4 len:65575 put:65575
> head:ffff888109ff0000 data:ffff888109ff0088 tail:0x100af end:0xfec0
> dev:<NULL>
> 
> Add a check that prevents an invalid packet with MTU equall to the
> fregment header size to eat up all the space for payload.

There probably ought to be a check much earlier that stops
the option that makes the iphdr being to big being accepted
in the first place.

Much better to do the check in the option generation code.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
Jakub Kicinski March 10, 2022, 10:30 p.m. UTC | #2
On Thu, 10 Mar 2022 14:13:28 -0800 Tadeusz Struk wrote:
> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> index 4788f6b37053..6d45112322a0 100644
> --- a/net/ipv6/ip6_output.c
> +++ b/net/ipv6/ip6_output.c
> @@ -1649,6 +1649,16 @@ static int __ip6_append_data(struct sock *sk,
>  			skb->protocol = htons(ETH_P_IPV6);
>  			skb->ip_summed = csummode;
>  			skb->csum = 0;
> +
> +			/*
> +			 *	Check if there is still room for payload
> +			 */

TBH I think the check is self-explanatory. Not worth a banner comment,
for sure.

> +			if (fragheaderlen >= mtu) {
> +				err = -EMSGSIZE;
> +				kfree_skb(skb);
> +				goto error;
> +			}

Not sure if Willem prefers this placement, but seems like we can lift
this check out of the loop, as soon as fragheaderlen and mtu are known.

>  			/* reserve for fragmentation and ipsec header */
>  			skb_reserve(skb, hh_len + sizeof(struct frag_hdr) +
>  				    dst_exthdrlen);
Tadeusz Struk March 10, 2022, 10:42 p.m. UTC | #3
On 3/10/22 14:30, Jakub Kicinski wrote:
>> +
>> +			/*
>> +			 *	Check if there is still room for payload
>> +			 */
> TBH I think the check is self-explanatory. Not worth a banner comment,
> for sure.

Ok

> 
>> +			if (fragheaderlen >= mtu) {
>> +				err = -EMSGSIZE;
>> +				kfree_skb(skb);
>> +				goto error;
>> +			}
> Not sure if Willem prefers this placement, but seems like we can lift
> this check out of the loop, as soon as fragheaderlen and mtu are known.
> 

He said to check it before the skb_put() and so I did.
The fragheaderlen is known early, but mtu can be updated inside the loop
by ip6_append_data_mtu() so I'm not sure we can do the check before that.
Willem de Bruijn March 10, 2022, 10:43 p.m. UTC | #4
On Thu, Mar 10, 2022 at 5:30 PM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Thu, 10 Mar 2022 14:13:28 -0800 Tadeusz Struk wrote:
> > diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> > index 4788f6b37053..6d45112322a0 100644
> > --- a/net/ipv6/ip6_output.c
> > +++ b/net/ipv6/ip6_output.c
> > @@ -1649,6 +1649,16 @@ static int __ip6_append_data(struct sock *sk,
> >                       skb->protocol = htons(ETH_P_IPV6);
> >                       skb->ip_summed = csummode;
> >                       skb->csum = 0;
> > +
> > +                     /*
> > +                      *      Check if there is still room for payload
> > +                      */
>
> TBH I think the check is self-explanatory. Not worth a banner comment,
> for sure.
>
> > +                     if (fragheaderlen >= mtu) {
> > +                             err = -EMSGSIZE;
> > +                             kfree_skb(skb);
> > +                             goto error;
> > +                     }
>
> Not sure if Willem prefers this placement, but seems like we can lift
> this check out of the loop, as soon as fragheaderlen and mtu are known.
>
> >                       /* reserve for fragmentation and ipsec header */
> >                       skb_reserve(skb, hh_len + sizeof(struct frag_hdr) +
> >                                   dst_exthdrlen);

Just updating this boundary check will do?

        if (mtu < fragheaderlen ||
            ((mtu - fragheaderlen) & ~7) + fragheaderlen <
sizeof(struct frag_hdr))
                goto emsgsize;
Tadeusz Struk March 10, 2022, 11:04 p.m. UTC | #5
On 3/10/22 14:43, Willem de Bruijn wrote:
> On Thu, Mar 10, 2022 at 5:30 PM Jakub Kicinski <kuba@kernel.org> wrote:
>>
>> On Thu, 10 Mar 2022 14:13:28 -0800 Tadeusz Struk wrote:
>>> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
>>> index 4788f6b37053..6d45112322a0 100644
>>> --- a/net/ipv6/ip6_output.c
>>> +++ b/net/ipv6/ip6_output.c
>>> @@ -1649,6 +1649,16 @@ static int __ip6_append_data(struct sock *sk,
>>>                        skb->protocol = htons(ETH_P_IPV6);
>>>                        skb->ip_summed = csummode;
>>>                        skb->csum = 0;
>>> +
>>> +                     /*
>>> +                      *      Check if there is still room for payload
>>> +                      */
>>
>> TBH I think the check is self-explanatory. Not worth a banner comment,
>> for sure.
>>
>>> +                     if (fragheaderlen >= mtu) {
>>> +                             err = -EMSGSIZE;
>>> +                             kfree_skb(skb);
>>> +                             goto error;
>>> +                     }
>>
>> Not sure if Willem prefers this placement, but seems like we can lift
>> this check out of the loop, as soon as fragheaderlen and mtu are known.
>>
>>>                        /* reserve for fragmentation and ipsec header */
>>>                        skb_reserve(skb, hh_len + sizeof(struct frag_hdr) +
>>>                                    dst_exthdrlen);
> 
> Just updating this boundary check will do?
> 
>          if (mtu < fragheaderlen ||
>              ((mtu - fragheaderlen) & ~7) + fragheaderlen <
> sizeof(struct frag_hdr))
>                  goto emsgsize;

Yes, it will. v3 on its way.
David Laight March 10, 2022, 11:05 p.m. UTC | #6
From: Willem de Bruijn
> Sent: 10 March 2022 22:43
> 
> On Thu, Mar 10, 2022 at 5:30 PM Jakub Kicinski <kuba@kernel.org> wrote:
> >
> > On Thu, 10 Mar 2022 14:13:28 -0800 Tadeusz Struk wrote:
> > > diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> > > index 4788f6b37053..6d45112322a0 100644
> > > --- a/net/ipv6/ip6_output.c
> > > +++ b/net/ipv6/ip6_output.c
> > > @@ -1649,6 +1649,16 @@ static int __ip6_append_data(struct sock *sk,
> > >                       skb->protocol = htons(ETH_P_IPV6);
> > >                       skb->ip_summed = csummode;
> > >                       skb->csum = 0;
> > > +
> > > +                     /*
> > > +                      *      Check if there is still room for payload
> > > +                      */
> >
> > TBH I think the check is self-explanatory. Not worth a banner comment,
> > for sure.
> >
> > > +                     if (fragheaderlen >= mtu) {
> > > +                             err = -EMSGSIZE;
> > > +                             kfree_skb(skb);
> > > +                             goto error;
> > > +                     }
> >
> > Not sure if Willem prefers this placement, but seems like we can lift
> > this check out of the loop, as soon as fragheaderlen and mtu are known.
> >
> > >                       /* reserve for fragmentation and ipsec header */
> > >                       skb_reserve(skb, hh_len + sizeof(struct frag_hdr) +
> > >                                   dst_exthdrlen);
> 
> Just updating this boundary check will do?
> 
>         if (mtu < fragheaderlen ||
>             ((mtu - fragheaderlen) & ~7) + fragheaderlen <
> sizeof(struct frag_hdr))
>                 goto emsgsize;

Both those < should be <=

But I think I'd check:
	if (fragheaderlen >= 1280 - sizeof (struct frag_hdr))
		goto emsgsize;
first (or only!)

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
diff mbox series

Patch

diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 4788f6b37053..6d45112322a0 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1649,6 +1649,16 @@  static int __ip6_append_data(struct sock *sk,
 			skb->protocol = htons(ETH_P_IPV6);
 			skb->ip_summed = csummode;
 			skb->csum = 0;
+
+			/*
+			 *	Check if there is still room for payload
+			 */
+			if (fragheaderlen >= mtu) {
+				err = -EMSGSIZE;
+				kfree_skb(skb);
+				goto error;
+			}
+
 			/* reserve for fragmentation and ipsec header */
 			skb_reserve(skb, hh_len + sizeof(struct frag_hdr) +
 				    dst_exthdrlen);