diff mbox

[net] net: ipv6: RTF_PCPU should not be settable from userspace

Message ID 1492636783-29756-1-git-send-email-dsa@cumulusnetworks.com
State Accepted, archived
Delegated to: David Miller
Headers show

Commit Message

David Ahern April 19, 2017, 9:19 p.m. UTC
Andrey reported a fault in the IPv6 route code:

kasan: GPF could be caused by NULL-ptr deref or user memory access
general protection fault: 0000 [#1] SMP KASAN
Modules linked in:
CPU: 1 PID: 4035 Comm: a.out Not tainted 4.11.0-rc7+ #250
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
task: ffff880069809600 task.stack: ffff880062dc8000
RIP: 0010:ip6_rt_cache_alloc+0xa6/0x560 net/ipv6/route.c:975
RSP: 0018:ffff880062dced30 EFLAGS: 00010206
RAX: dffffc0000000000 RBX: ffff8800670561c0 RCX: 0000000000000006
RDX: 0000000000000003 RSI: ffff880062dcfb28 RDI: 0000000000000018
RBP: ffff880062dced68 R08: 0000000000000001 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
R13: ffff880062dcfb28 R14: dffffc0000000000 R15: 0000000000000000
FS:  00007feebe37e7c0(0000) GS:ffff88006cb00000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00000000205a0fe4 CR3: 000000006b5c9000 CR4: 00000000000006e0
Call Trace:
 ip6_pol_route+0x1512/0x1f20 net/ipv6/route.c:1128
 ip6_pol_route_output+0x4c/0x60 net/ipv6/route.c:1212
...

Andrey's syzkaller program passes rtmsg.rtmsg_flags with the RTF_PCPU bit
set. Flags passed to the kernel are blindly copied to the allocated
rt6_info by ip6_route_info_create making a newly inserted route appear
as though it is a per-cpu route. ip6_rt_cache_alloc sees the flag set
and expects rt->dst.from to be set - which it is not since it is not
really a per-cpu copy. The subsequent call to __ip6_dst_alloc then
generates the fault.

Fix by checking for the flag and failing with EINVAL.

Fixes: d52d3997f843f ("ipv6: Create percpu rt6_info")
Reported-by: Andrey Konovalov <andreyknvl@google.com>
Signed-off-by: David Ahern <dsa@cumulusnetworks.com>
---
 include/uapi/linux/ipv6_route.h | 2 +-
 net/ipv6/route.c                | 4 ++++
 2 files changed, 5 insertions(+), 1 deletion(-)

Comments

Martin KaFai Lau April 19, 2017, 9:52 p.m. UTC | #1
On Wed, Apr 19, 2017 at 02:19:43PM -0700, David Ahern wrote:
> Andrey reported a fault in the IPv6 route code:
>
> kasan: GPF could be caused by NULL-ptr deref or user memory access
> general protection fault: 0000 [#1] SMP KASAN
> Modules linked in:
> CPU: 1 PID: 4035 Comm: a.out Not tainted 4.11.0-rc7+ #250
> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
> task: ffff880069809600 task.stack: ffff880062dc8000
> RIP: 0010:ip6_rt_cache_alloc+0xa6/0x560 net/ipv6/route.c:975
> RSP: 0018:ffff880062dced30 EFLAGS: 00010206
> RAX: dffffc0000000000 RBX: ffff8800670561c0 RCX: 0000000000000006
> RDX: 0000000000000003 RSI: ffff880062dcfb28 RDI: 0000000000000018
> RBP: ffff880062dced68 R08: 0000000000000001 R09: 0000000000000000
> R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
> R13: ffff880062dcfb28 R14: dffffc0000000000 R15: 0000000000000000
> FS:  00007feebe37e7c0(0000) GS:ffff88006cb00000(0000) knlGS:0000000000000000
> CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 00000000205a0fe4 CR3: 000000006b5c9000 CR4: 00000000000006e0
> Call Trace:
>  ip6_pol_route+0x1512/0x1f20 net/ipv6/route.c:1128
>  ip6_pol_route_output+0x4c/0x60 net/ipv6/route.c:1212
> ...
>
> Andrey's syzkaller program passes rtmsg.rtmsg_flags with the RTF_PCPU bit
> set. Flags passed to the kernel are blindly copied to the allocated
> rt6_info by ip6_route_info_create making a newly inserted route appear
> as though it is a per-cpu route. ip6_rt_cache_alloc sees the flag set
> and expects rt->dst.from to be set - which it is not since it is not
> really a per-cpu copy. The subsequent call to __ip6_dst_alloc then
> generates the fault.
>
> Fix by checking for the flag and failing with EINVAL.
Thanks for the fix.

Acked-by: Martin KaFai Lau <kafai@fb.com>

>
> Fixes: d52d3997f843f ("ipv6: Create percpu rt6_info")
> Reported-by: Andrey Konovalov <andreyknvl@google.com>
> Signed-off-by: David Ahern <dsa@cumulusnetworks.com>
> ---
>  include/uapi/linux/ipv6_route.h | 2 +-
>  net/ipv6/route.c                | 4 ++++
>  2 files changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/include/uapi/linux/ipv6_route.h b/include/uapi/linux/ipv6_route.h
> index 85bbb1799df3..d496c02e14bc 100644
> --- a/include/uapi/linux/ipv6_route.h
> +++ b/include/uapi/linux/ipv6_route.h
> @@ -35,7 +35,7 @@
>  #define RTF_PREF(pref)	((pref) << 27)
>  #define RTF_PREF_MASK	0x18000000
>
> -#define RTF_PCPU	0x40000000
> +#define RTF_PCPU	0x40000000	/* read-only: can not be set by user */
>  #define RTF_LOCAL	0x80000000
>
>
> diff --git a/net/ipv6/route.c b/net/ipv6/route.c
> index 4ba7c49872ff..a1bf426c959b 100644
> --- a/net/ipv6/route.c
> +++ b/net/ipv6/route.c
> @@ -1854,6 +1854,10 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg)
>  	int addr_type;
>  	int err = -EINVAL;
>
> +	/* RTF_PCPU is an internal flag; can not be set by userspace */
> +	if (cfg->fc_flags & RTF_PCPU)
> +		goto out;
> +
>  	if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
>  		goto out;
>  #ifndef CONFIG_IPV6_SUBTREES
> --
> 2.9.3
>
Andrey Konovalov April 20, 2017, 12:09 p.m. UTC | #2
Thanks!

Tested-by: Andrey Konovalov <andreyknvl@google.com>

On Wed, Apr 19, 2017 at 11:52 PM, Martin KaFai Lau <kafai@fb.com> wrote:
> On Wed, Apr 19, 2017 at 02:19:43PM -0700, David Ahern wrote:
>> Andrey reported a fault in the IPv6 route code:
>>
>> kasan: GPF could be caused by NULL-ptr deref or user memory access
>> general protection fault: 0000 [#1] SMP KASAN
>> Modules linked in:
>> CPU: 1 PID: 4035 Comm: a.out Not tainted 4.11.0-rc7+ #250
>> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
>> task: ffff880069809600 task.stack: ffff880062dc8000
>> RIP: 0010:ip6_rt_cache_alloc+0xa6/0x560 net/ipv6/route.c:975
>> RSP: 0018:ffff880062dced30 EFLAGS: 00010206
>> RAX: dffffc0000000000 RBX: ffff8800670561c0 RCX: 0000000000000006
>> RDX: 0000000000000003 RSI: ffff880062dcfb28 RDI: 0000000000000018
>> RBP: ffff880062dced68 R08: 0000000000000001 R09: 0000000000000000
>> R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
>> R13: ffff880062dcfb28 R14: dffffc0000000000 R15: 0000000000000000
>> FS:  00007feebe37e7c0(0000) GS:ffff88006cb00000(0000) knlGS:0000000000000000
>> CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
>> CR2: 00000000205a0fe4 CR3: 000000006b5c9000 CR4: 00000000000006e0
>> Call Trace:
>>  ip6_pol_route+0x1512/0x1f20 net/ipv6/route.c:1128
>>  ip6_pol_route_output+0x4c/0x60 net/ipv6/route.c:1212
>> ...
>>
>> Andrey's syzkaller program passes rtmsg.rtmsg_flags with the RTF_PCPU bit
>> set. Flags passed to the kernel are blindly copied to the allocated
>> rt6_info by ip6_route_info_create making a newly inserted route appear
>> as though it is a per-cpu route. ip6_rt_cache_alloc sees the flag set
>> and expects rt->dst.from to be set - which it is not since it is not
>> really a per-cpu copy. The subsequent call to __ip6_dst_alloc then
>> generates the fault.
>>
>> Fix by checking for the flag and failing with EINVAL.
> Thanks for the fix.
>
> Acked-by: Martin KaFai Lau <kafai@fb.com>
>
>>
>> Fixes: d52d3997f843f ("ipv6: Create percpu rt6_info")
>> Reported-by: Andrey Konovalov <andreyknvl@google.com>
>> Signed-off-by: David Ahern <dsa@cumulusnetworks.com>
>> ---
>>  include/uapi/linux/ipv6_route.h | 2 +-
>>  net/ipv6/route.c                | 4 ++++
>>  2 files changed, 5 insertions(+), 1 deletion(-)
>>
>> diff --git a/include/uapi/linux/ipv6_route.h b/include/uapi/linux/ipv6_route.h
>> index 85bbb1799df3..d496c02e14bc 100644
>> --- a/include/uapi/linux/ipv6_route.h
>> +++ b/include/uapi/linux/ipv6_route.h
>> @@ -35,7 +35,7 @@
>>  #define RTF_PREF(pref)       ((pref) << 27)
>>  #define RTF_PREF_MASK        0x18000000
>>
>> -#define RTF_PCPU     0x40000000
>> +#define RTF_PCPU     0x40000000      /* read-only: can not be set by user */
>>  #define RTF_LOCAL    0x80000000
>>
>>
>> diff --git a/net/ipv6/route.c b/net/ipv6/route.c
>> index 4ba7c49872ff..a1bf426c959b 100644
>> --- a/net/ipv6/route.c
>> +++ b/net/ipv6/route.c
>> @@ -1854,6 +1854,10 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg)
>>       int addr_type;
>>       int err = -EINVAL;
>>
>> +     /* RTF_PCPU is an internal flag; can not be set by userspace */
>> +     if (cfg->fc_flags & RTF_PCPU)
>> +             goto out;
>> +
>>       if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
>>               goto out;
>>  #ifndef CONFIG_IPV6_SUBTREES
>> --
>> 2.9.3
>>
Cong Wang April 20, 2017, 10:39 p.m. UTC | #3
On Wed, Apr 19, 2017 at 2:19 PM, David Ahern <dsa@cumulusnetworks.com> wrote:
>
> Fix by checking for the flag and failing with EINVAL.
>

I am still not sure about this. There are a few unused bits in
this flag, we simply ignore the rest, right? Why should we
reject this one instead of all of those we don't use?
David Ahern April 20, 2017, 10:43 p.m. UTC | #4
On 4/20/17 4:39 PM, Cong Wang wrote:
> On Wed, Apr 19, 2017 at 2:19 PM, David Ahern <dsa@cumulusnetworks.com> wrote:
>>
>> Fix by checking for the flag and failing with EINVAL.
>>
> 
> I am still not sure about this. There are a few unused bits in
> this flag, we simply ignore the rest, right? Why should we
> reject this one instead of all of those we don't use?
> 

RTF_PCPU most definitely should not be set by userspace. arguably it
should not be returned to userspace either, but it is part of the uapi.


I scanned the others. It is not clear that others should fail with
EINVAL. Certainly a mask of unused flags can be added, but to me that is
on top of this bug fix.
Cong Wang April 20, 2017, 11:37 p.m. UTC | #5
On Thu, Apr 20, 2017 at 3:43 PM, David Ahern <dsa@cumulusnetworks.com> wrote:
>
> I scanned the others. It is not clear that others should fail with
> EINVAL. Certainly a mask of unused flags can be added, but to me that is
> on top of this bug fix.
>

If we want to preserve those unused bits, we should reject them too.

RTF_PCPU is special here, it is used but only internally, so it makes
sense to silently clear it since we don't care whether people set it to
1 or 0. We should clear it for dumping too since it is internal only.
Martin KaFai Lau April 20, 2017, 11:38 p.m. UTC | #6
On Thu, Apr 20, 2017 at 04:43:03PM -0600, David Ahern wrote:
> On 4/20/17 4:39 PM, Cong Wang wrote:
> > On Wed, Apr 19, 2017 at 2:19 PM, David Ahern <dsa@cumulusnetworks.com> wrote:
> >>
> >> Fix by checking for the flag and failing with EINVAL.
> >>
> >
> > I am still not sure about this. There are a few unused bits in
> > this flag, we simply ignore the rest, right? Why should we
> > reject this one instead of all of those we don't use?
> >
>
> RTF_PCPU most definitely should not be set by userspace.
+1
Martin KaFai Lau April 21, 2017, 12:16 a.m. UTC | #7
On Thu, Apr 20, 2017 at 04:37:18PM -0700, Cong Wang wrote:
> On Thu, Apr 20, 2017 at 3:43 PM, David Ahern <dsa@cumulusnetworks.com> wrote:
> >
> > I scanned the others. It is not clear that others should fail with
> > EINVAL. Certainly a mask of unused flags can be added, but to me that is
> > on top of this bug fix.
> >
>
> If we want to preserve those unused bits, we should reject them too.
>
> RTF_PCPU is special here, it is used but only internally, so it makes
> sense to silently clear it since we don't care whether people set it to
> 1 or 0. We should clear it for dumping too since it is internal only.
I agree with DavidA. The existing bits (including RTF_PCPU) during dumping
is part of the uapi already.  We cannot stop displaying them now.

Silently accepting something instead of telling the userspace program
has a bug seems to be a dis-service to the end-user.

If there are other bits should be rejected too, they can be
done in the follow up patches.
David Miller April 21, 2017, 5:56 p.m. UTC | #8
From: David Ahern <dsa@cumulusnetworks.com>
Date: Wed, 19 Apr 2017 14:19:43 -0700

> Andrey reported a fault in the IPv6 route code:
> 
> kasan: GPF could be caused by NULL-ptr deref or user memory access
> general protection fault: 0000 [#1] SMP KASAN
> Modules linked in:
> CPU: 1 PID: 4035 Comm: a.out Not tainted 4.11.0-rc7+ #250
> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
> task: ffff880069809600 task.stack: ffff880062dc8000
> RIP: 0010:ip6_rt_cache_alloc+0xa6/0x560 net/ipv6/route.c:975
> RSP: 0018:ffff880062dced30 EFLAGS: 00010206
> RAX: dffffc0000000000 RBX: ffff8800670561c0 RCX: 0000000000000006
> RDX: 0000000000000003 RSI: ffff880062dcfb28 RDI: 0000000000000018
> RBP: ffff880062dced68 R08: 0000000000000001 R09: 0000000000000000
> R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
> R13: ffff880062dcfb28 R14: dffffc0000000000 R15: 0000000000000000
> FS:  00007feebe37e7c0(0000) GS:ffff88006cb00000(0000) knlGS:0000000000000000
> CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 00000000205a0fe4 CR3: 000000006b5c9000 CR4: 00000000000006e0
> Call Trace:
>  ip6_pol_route+0x1512/0x1f20 net/ipv6/route.c:1128
>  ip6_pol_route_output+0x4c/0x60 net/ipv6/route.c:1212
> ...
> 
> Andrey's syzkaller program passes rtmsg.rtmsg_flags with the RTF_PCPU bit
> set. Flags passed to the kernel are blindly copied to the allocated
> rt6_info by ip6_route_info_create making a newly inserted route appear
> as though it is a per-cpu route. ip6_rt_cache_alloc sees the flag set
> and expects rt->dst.from to be set - which it is not since it is not
> really a per-cpu copy. The subsequent call to __ip6_dst_alloc then
> generates the fault.
> 
> Fix by checking for the flag and failing with EINVAL.
> 
> Fixes: d52d3997f843f ("ipv6: Create percpu rt6_info")
> Reported-by: Andrey Konovalov <andreyknvl@google.com>
> Signed-off-by: David Ahern <dsa@cumulusnetworks.com>

Applied and queued up for -stable.
diff mbox

Patch

diff --git a/include/uapi/linux/ipv6_route.h b/include/uapi/linux/ipv6_route.h
index 85bbb1799df3..d496c02e14bc 100644
--- a/include/uapi/linux/ipv6_route.h
+++ b/include/uapi/linux/ipv6_route.h
@@ -35,7 +35,7 @@ 
 #define RTF_PREF(pref)	((pref) << 27)
 #define RTF_PREF_MASK	0x18000000
 
-#define RTF_PCPU	0x40000000
+#define RTF_PCPU	0x40000000	/* read-only: can not be set by user */
 #define RTF_LOCAL	0x80000000
 
 
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 4ba7c49872ff..a1bf426c959b 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -1854,6 +1854,10 @@  static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg)
 	int addr_type;
 	int err = -EINVAL;
 
+	/* RTF_PCPU is an internal flag; can not be set by userspace */
+	if (cfg->fc_flags & RTF_PCPU)
+		goto out;
+
 	if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128)
 		goto out;
 #ifndef CONFIG_IPV6_SUBTREES