2012-08-07 18:12:47

by John Stultz

[permalink] [raw]
Subject: NULL pointer dereference in selinux_ip_postroute_compat

Hi,
With my kvm environment using 3.6-rc1+, I'm seeing NULL pointer
dereferences in selinux_ip_postroute_compat(). It looks like the sksec
value is null and we die in the following line:

if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))

This triggers every time I shutdown the machine, but has also triggered
randomly after a few hours.

This is on an ubuntu 12.04 image, not using selinux.

Running with the following kvm line:
kvm -nographic -smp 4 -m 1G -hda disk.img -net user -net
nic,model=virtio -redir tcp:4400::22 -kernel ./bzImage -initrd
initrd.img-1-jstultz -append
"root=UUID=b08aa86a-4b16-488f-a3de-33c2cf335bf0 ro console=ttyS0,115200n8"

Two different traces below. Config attached.

thanks
-john

Trace1 @ shutdown:

[ 69.272927] BUG: unable to handle kernel NULL pointer dereference at 0000000000000010
[ 69.273374] IP: [<ffffffff8132e7c4>] selinux_ip_postroute_compat+0xa4/0xe0
[ 69.273374] PGD 3a85b067 PUD 3f50b067 PMD 0
[ 69.273374] Oops: 0000 [#1] PREEMPT SMP
[ 69.273374] CPU 3
[ 69.273374] Pid: 2392, comm: hwclock Not tainted 3.6.0-rc1john+ #106 Bochs Bochs
[ 69.273374] RIP: 0010:[<ffffffff8132e7c4>] [<ffffffff8132e7c4>] selinux_ip_postroute_compat+0xa4/0xe0
[ 69.273374] RSP: 0018:ffff88003f003720 EFLAGS: 00010246
[ 69.273374] RAX: 0000000000000000 RBX: ffff88003f5fa9d8 RCX: 0000000000000006
[ 69.273374] RDX: ffff88003f003740 RSI: ffff88003c6b256c RDI: ffff88003f5fa9d8
[ OK ]
[ 69.273374] RBP: ffff88003f0037a0 R08: 0000000000000000 R09: ffff88003f1d0cc0
[ 69.273374] R10: 0000000000000003 R11: 0000000000000000 R12: 0000000000000000
[ 69.273374] R13: 0000000000000002 R14: ffff88003f0037c0 R15: 0000000000000004
[ 69.273374] FS: 00007fa398211700(0000) GS:ffff88003f000000(0000) knlGS:0000000000000000
[ 69.273374] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 69.273374] CR2: 0000000000000010 CR3: 000000003b52a000 CR4: 00000000000006e0
[ 69.273374] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 69.273374] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[ 69.273374] Process hwclock (pid: 2392, threadinfo ffff88003a0ee000, task ffff88003fa82b80)
[ 69.273374] Stack:
[ 69.273374] ffff88003c6b2558 0000000000000006 0000000000000000 0000160067d70002
[ 69.273374] 0f02000a0202000a 0000000000000000 0000000000000000 0000000000000000
[ 69.273374] ffff88003f003802 ffff88003f003728 ffff88003f1d42d0 ffff88003d6c3560
[ 69.273374] Call Trace:
[ 69.273374] <IRQ>
[ 69.273374] [<ffffffff8132eaab>] selinux_ip_postroute+0x2ab/0x3e0
[ 69.273374] [<ffffffff8132ec1c>] selinux_ipv4_postroute+0x1c/0x20
[ 69.273374] [<ffffffff8198265c>] nf_iterate+0xac/0x140
[ 69.273374] [<ffffffff8199be00>] ? ip_fragment+0xa20/0xa20
[ 69.273374] [<ffffffff819827a5>] nf_hook_slow+0xb5/0x210
[ 69.273374] [<ffffffff8199be00>] ? ip_fragment+0xa20/0xa20
[ 69.273374] [<ffffffff8199cbba>] ip_output+0xaa/0x150
[ 69.273374] [<ffffffff8199a9af>] ip_local_out+0x7f/0x110
[ 69.273374] [<ffffffff8199d82e>] ip_send_skb+0xe/0x40
[ 69.273374] [<ffffffff8199d88b>] ip_push_pending_frames+0x2b/0x30
[ 69.273374] [<ffffffff8199dc97>] ip_send_unicast_reply+0x2c7/0x3c0
[ 69.273374] [<ffffffff8117e275>] ? kmem_cache_free+0x285/0x3e0
[ 69.273374] [<ffffffff819bb215>] tcp_v4_send_reset+0x1f5/0x3f0
[ 69.273374] [<ffffffff819bf04b>] tcp_v4_rcv+0x2bb/0x1080
[ 69.273374] [<ffffffff81994d73>] ip_local_deliver_finish+0x133/0x4d0
[ 69.273374] [<ffffffff81994c9c>] ? ip_local_deliver_finish+0x5c/0x4d0
[ 69.273374] [<ffffffff819953e0>] ip_local_deliver+0x90/0xa0
[ 69.273374] [<ffffffff819945b2>] ip_rcv_finish+0x262/0x8f0
[ 69.273374] [<ffffffff81995742>] ip_rcv+0x352/0x3a0
[ 69.323844] [<ffffffff81925244>] __netif_receive_skb+0xcb4/0x10e0
[ 69.323844] [<ffffffff81924899>] ? __netif_receive_skb+0x309/0x10e0
[ 69.323844] [<ffffffff8117c176>] ? kmem_cache_alloc+0x256/0x4e0
[ 69.323844] [<ffffffff81917c24>] ? build_skb+0x34/0x1c0
[ 69.323844] [<ffffffff8192ba5d>] netif_receive_skb+0x18d/0x230
[ 69.323844] [<ffffffff81951da8>] ? eth_type_trans+0x168/0x190
[ 69.323844] [<ffffffff81746abc>] virtnet_poll+0x58c/0x7b0
[ 69.323844] [<ffffffff8192cf59>] net_rx_action+0x289/0x550
[ 69.323844] [<ffffffff8105846a>] __do_softirq+0x1da/0x560
[ 69.323844] [<ffffffff810ed8fb>] ? handle_edge_irq+0x12b/0x190
[ 69.323844] [<ffffffff81b5c2bc>] call_softirq+0x1c/0x30
[ 69.323844] [<ffffffff81004d75>] do_softirq+0x105/0x1e0
[ 69.323844] [<ffffffff81058bbe>] irq_exit+0x9e/0x100
[ 69.323844] [<ffffffff81b5c9d3>] do_IRQ+0x63/0xd0
[ 69.323844] [<ffffffff81b5a56f>] common_interrupt+0x6f/0x6f
[ 69.323844] <EOI>
[ 69.323844] [<ffffffff810b964e>] ? put_lock_stats.isra.19+0xe/0x40
[ 69.323844] [<ffffffff81115363>] ? ftrace_likely_update+0xf3/0x250
[ 69.323844] [<ffffffff810993ad>] __might_sleep+0x1cd/0x280
[ 69.323844] [<ffffffff810ad6fc>] ? getnstimeofday+0xdc/0x150
[ 69.323844] [<ffffffff81160e74>] might_fault+0x34/0xb0
[ 69.323844] [<ffffffff8105657e>] sys_gettimeofday+0xbe/0xf0
[ 69.323844] [<ffffffff81b5afe9>] system_call_fastpath+0x16/0x1b
[ 69.323844] Code: c0 45 31 c9 b1 01 ba 2a 00 00 00 e8 a7 89 ff ff 85 c0 b9 00 00 6f 00 74 0e 48 83 c4 70 89 c8 5b 41 5c 5d c3 0f 1f 00 0f b6 4d ef <41> 8b 7c 24 10 48 8d 55 c0 48 89 de e8 ab 6d 01 00 83 f8 01 19
[ 69.323844] RIP [<ffffffff8132e7c4>] selinux_ip_postroute_compat+0xa4/0xe0
[ 69.323844] RSP <ffff88003f003720>
[ 69.323844] CR2: 0000000000000010
[ 69.357489] ---[ end trace 0cd3e1a60dee6096 ]---
[ 69.358353] Kernel panic - not syncing: Fatal exception in interrupt


Trace2: After some uptime

[17169.735267] BUG: unable to handle kernel NULL pointer dereference at 0000000000000010
[17169.738338] IP: [<ffffffff8132e7c4>] selinux_ip_postroute_compat+0xa4/0xe0
[17169.738338] PGD 39a97067 PUD 3cc09067 PMD 0
[17169.738338] Oops: 0000 [#1] PREEMPT SMP
[17169.738338] CPU 3
[17169.738338] Pid: 0, comm: swapper/3 Not tainted 3.6.0-rc1john+ #106 Bochs Bochs
[17169.738338] RIP: 0010:[<ffffffff8132e7c4>] [<ffffffff8132e7c4>] selinux_ip_postroute_compat+0xa4/0xe0
[17169.738338] RSP: 0018:ffff88003f003700 EFLAGS: 00010246
[17169.738338] RAX: 0000000000000000 RBX: ffff88003a0ffd98 RCX: 0000000000000006
[17169.738338] RDX: ffff88003f003720 RSI: ffff88003980c2d4 RDI: ffff88003a0ffd98
[17169.738338] RBP: ffff88003f003780 R08: 0000000000000000 R09: ffff88003f1d0cc0
[17169.738338] R10: 0000000000000003 R11: 0000000000000000 R12: 0000000000000000
[17169.738338] R13: 0000000000000002 R14: ffff88003f0037a0 R15: 0000000000000004
[17169.738338] FS: 0000000000000000(0000) GS:ffff88003f000000(0000) knlGS:0000000000000000
[17169.738338] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
[17169.738338] CR2: 0000000000000010 CR3: 0000000039bb7000 CR4: 00000000000006e0
[17169.738338] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[17169.738338] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[17169.738338] Process swapper/3 (pid: 0, threadinfo ffff88003d4da000, task ffff88003d4d82c0)
[17169.738338] Stack:
[17169.738338] ffff88003980c2c0 0000000000000006 0000000000000000 000034b450000002
[17169.738338] 0f02000a1e5bbd5b 0000000000000000 0000000000000000 0000000000000000
[17169.738338] ffff88003f003802 ffff88003f003708 ffff88003f1d42d0 ffff88003d719db0
[17169.738338] Call Trace:
[17169.738338] <IRQ>
[17169.738338] [<ffffffff8132eaab>] selinux_ip_postroute+0x2ab/0x3e0
[17169.738338] [<ffffffff8132ec1c>] selinux_ipv4_postroute+0x1c/0x20
[17169.738338] [<ffffffff8198265c>] nf_iterate+0xac/0x140
[17169.738338] [<ffffffff8199be00>] ? ip_fragment+0xa20/0xa20
[17169.738338] [<ffffffff819827a5>] nf_hook_slow+0xb5/0x210
[17169.738338] [<ffffffff8199be00>] ? ip_fragment+0xa20/0xa20
[17169.738338] [<ffffffff8199cbba>] ip_output+0xaa/0x150
[17169.738338] [<ffffffff8199a9af>] ip_local_out+0x7f/0x110
[17169.738338] [<ffffffff8199d82e>] ip_send_skb+0xe/0x40
[17169.738338] [<ffffffff8199d88b>] ip_push_pending_frames+0x2b/0x30
[17169.738338] [<ffffffff8199dc97>] ip_send_unicast_reply+0x2c7/0x3c0
[17169.738338] [<ffffffff810b9744>] ? lock_release_holdtime.part.20+0xc4/0x160
[17169.738338] [<ffffffff819bd1f6>] tcp_v4_send_ack.isra.33+0x176/0x280
[17169.738338] [<ffffffff8191f90a>] ? __skb_checksum_complete_head+0x8a/0xc0
[17169.738338] [<ffffffff819bf191>] tcp_v4_rcv+0x401/0x1080
[17169.738338] [<ffffffff81994d73>] ip_local_deliver_finish+0x133/0x4d0
[17169.738338] [<ffffffff81994c9c>] ? ip_local_deliver_finish+0x5c/0x4d0
[17169.738338] [<ffffffff819953e0>] ip_local_deliver+0x90/0xa0
[17169.738338] [<ffffffff819945b2>] ip_rcv_finish+0x262/0x8f0
[17169.738338] [<ffffffff81995742>] ip_rcv+0x352/0x3a0
[17169.738338] [<ffffffff81925244>] __netif_receive_skb+0xcb4/0x10e0
[17169.738338] [<ffffffff81924899>] ? __netif_receive_skb+0x309/0x10e0
[17169.738338] [<ffffffff8192ba5d>] netif_receive_skb+0x18d/0x230
[17169.738338] [<ffffffff81951da8>] ? eth_type_trans+0x168/0x190
[17169.738338] [<ffffffff81746abc>] virtnet_poll+0x58c/0x7b0
[17169.738338] [<ffffffff8192cf59>] net_rx_action+0x289/0x550
[17169.738338] [<ffffffff8105846a>] __do_softirq+0x1da/0x560
[17169.738338] [<ffffffff81b5c2bc>] call_softirq+0x1c/0x30
[17169.738338] [<ffffffff81004d75>] do_softirq+0x105/0x1e0
[17169.738338] [<ffffffff81058bbe>] irq_exit+0x9e/0x100
[17169.738338] [<ffffffff81b5caab>] smp_apic_timer_interrupt+0x6b/0x98
[17169.738338] [<ffffffff81b5bb2f>] apic_timer_interrupt+0x6f/0x80
[17169.738338] <EOI>
[17169.738338] [<ffffffff81037d66>] ? native_safe_halt+0x6/0x10
[17169.738338] [<ffffffff8100e5af>] default_idle+0x76f/0x780
[17169.738338] [<ffffffff8100f3e6>] cpu_idle+0x136/0x140
[17169.738338] [<ffffffff81b3aab2>] start_secondary+0x1cf/0x1d4
[17169.738338] Code: c0 45 31 c9 b1 01 ba 2a 00 00 00 e8 a7 89 ff ff 85 c0 b9 00 00 6f 00 74 0e 48 83 c4 70 89 c8 5b 41 5c 5d c3 0f 1f 00 0f b6 4d ef <41> 8b 7c 24 10 48 8d 55 c0 48 89 de e8 ab 6d 01 00 83 f8 01 19
[17169.738338] RIP [<ffffffff8132e7c4>] selinux_ip_postroute_compat+0xa4/0xe0
[17169.738338] RSP <ffff88003f003700>
[17169.738338] CR2: 0000000000000010
[17169.829670] ---[ end trace a3af16e2baf5b40e ]---
[17169.830629] Kernel panic - not syncing: Fatal exception in interrupt


Attachments:
.config (81.73 kB)

2012-08-07 21:50:25

by Paul Moore

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Tue, Aug 7, 2012 at 2:12 PM, John Stultz <[email protected]> wrote:
> Hi,
> With my kvm environment using 3.6-rc1+, I'm seeing NULL pointer
> dereferences in selinux_ip_postroute_compat(). It looks like the sksec value
> is null and we die in the following line:
>
> if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
>
> This triggers every time I shutdown the machine, but has also triggered
> randomly after a few hours.
>
> This is on an ubuntu 12.04 image, not using selinux.

NOTE: Adding the SELinux list to the CC line

Hi,

I'm trying to understand this and I was hoping you could you clarify a
few things for me:

* Is the panic in the Ubuntu 12.04 guest, or the host? If the host,
could you share what distribution you are using?
* When you say you are not using SELinux, could you be more specific?
It seems odd that you are not using SELinux but the panic is happening
in a SELinux hook.

Thanks.

> Running with the following kvm line:
> kvm -nographic -smp 4 -m 1G -hda disk.img -net user -net nic,model=virtio
> -redir tcp:4400::22 -kernel ./bzImage -initrd initrd.img-1-jstultz -append
> "root=UUID=b08aa86a-4b16-488f-a3de-33c2cf335bf0 ro console=ttyS0,115200n8"
>
> Two different traces below. Config attached.

--
paul moore
http://www.paul-moore.com

2012-08-07 21:58:45

by John Stultz

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On 08/07/2012 02:50 PM, Paul Moore wrote:
> On Tue, Aug 7, 2012 at 2:12 PM, John Stultz <[email protected]> wrote:
>> Hi,
>> With my kvm environment using 3.6-rc1+, I'm seeing NULL pointer
>> dereferences in selinux_ip_postroute_compat(). It looks like the sksec value
>> is null and we die in the following line:
>>
>> if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
>>
>> This triggers every time I shutdown the machine, but has also triggered
>> randomly after a few hours.
>>
>> This is on an ubuntu 12.04 image, not using selinux.
> NOTE: Adding the SELinux list to the CC line
Thanks!

>
> Hi,
>
> I'm trying to understand this and I was hoping you could you clarify a
> few things for me:
>
> * Is the panic in the Ubuntu 12.04 guest, or the host? If the host,
> could you share what distribution you are using?
Sorry, its a 12.04 guest. I think the host is Ubuntu 12.04 as well.

> * When you say you are not using SELinux, could you be more specific?
> It seems odd that you are not using SELinux but the panic is happening
> in a SELinux hook.
I just mean that, being Ubuntu, the system (userland) isn't configured
to use selinux. SELinux is just enabled in the kernel config.

thanks
-john

2012-08-07 22:01:40

by Paul Moore

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Tue, Aug 7, 2012 at 5:58 PM, John Stultz <[email protected]> wrote:
> On 08/07/2012 02:50 PM, Paul Moore wrote:
>>
>> On Tue, Aug 7, 2012 at 2:12 PM, John Stultz <[email protected]>
>> wrote:
>>>
>>> Hi,
>>> With my kvm environment using 3.6-rc1+, I'm seeing NULL pointer
>>> dereferences in selinux_ip_postroute_compat(). It looks like the sksec
>>> value
>>> is null and we die in the following line:
>>>
>>> if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
>>>
>>> This triggers every time I shutdown the machine, but has also triggered
>>> randomly after a few hours.
>>>
>>> This is on an ubuntu 12.04 image, not using selinux.
>>
>> NOTE: Adding the SELinux list to the CC line
>
> Thanks!
>
>> Hi,
>>
>> I'm trying to understand this and I was hoping you could you clarify a
>> few things for me:
>>
>> * Is the panic in the Ubuntu 12.04 guest, or the host? If the host,
>> could you share what distribution you are using?
>
> Sorry, its a 12.04 guest. I think the host is Ubuntu 12.04 as well.
>
>
>> * When you say you are not using SELinux, could you be more specific?
>> It seems odd that you are not using SELinux but the panic is happening
>> in a SELinux hook.
>
> I just mean that, being Ubuntu, the system (userland) isn't configured to
> use selinux. SELinux is just enabled in the kernel config.

Thanks for the quick response, I'll setup an Ubuntu guest and see if I
can reproduce this ... something is odd. Anything non-standard about
your guest install or anything else you think might be helpful?

--
paul moore
http://www.paul-moore.com

2012-08-07 22:16:21

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

Quoting Paul Moore ([email protected]):
> On Tue, Aug 7, 2012 at 5:58 PM, John Stultz <[email protected]> wrote:
> > On 08/07/2012 02:50 PM, Paul Moore wrote:
> >>
> >> On Tue, Aug 7, 2012 at 2:12 PM, John Stultz <[email protected]>
> >> wrote:
> >>>
> >>> Hi,
> >>> With my kvm environment using 3.6-rc1+, I'm seeing NULL pointer
> >>> dereferences in selinux_ip_postroute_compat(). It looks like the sksec
> >>> value
> >>> is null and we die in the following line:
> >>>
> >>> if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
> >>>
> >>> This triggers every time I shutdown the machine, but has also triggered
> >>> randomly after a few hours.
> >>>
> >>> This is on an ubuntu 12.04 image, not using selinux.
> >>
> >> NOTE: Adding the SELinux list to the CC line
> >
> > Thanks!
> >
> >> Hi,
> >>
> >> I'm trying to understand this and I was hoping you could you clarify a
> >> few things for me:
> >>
> >> * Is the panic in the Ubuntu 12.04 guest, or the host? If the host,
> >> could you share what distribution you are using?
> >
> > Sorry, its a 12.04 guest. I think the host is Ubuntu 12.04 as well.
> >
> >
> >> * When you say you are not using SELinux, could you be more specific?
> >> It seems odd that you are not using SELinux but the panic is happening
> >> in a SELinux hook.
> >
> > I just mean that, being Ubuntu, the system (userland) isn't configured to
> > use selinux. SELinux is just enabled in the kernel config.
>
> Thanks for the quick response, I'll setup an Ubuntu guest and see if I
> can reproduce this ... something is odd. Anything non-standard about
> your guest install or anything else you think might be helpful?

The problem seems to be that selinux_nf_ip_init() was called, which
registers the selinux_ipv4_ops (and ipv6). Those should not get registered
if selinux ends up not being loaded (as in, if apparmor is loaded first),
since as you've found here the selinux lsm hooks won't be called to set
call selinux_sk_alloc_security().

I assume what's happening is that CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE was
set to 1, but selinux ended up being set to disabled after the
__initcall(selinux_nf_ip_init) ran? Weird.

-serge

2012-08-07 22:24:01

by Paul Moore

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Tuesday, August 07, 2012 10:17:32 PM Serge E. Hallyn wrote:
> Quoting Paul Moore ([email protected]):
> > On Tue, Aug 7, 2012 at 5:58 PM, John Stultz <[email protected]>
wrote:
> > > On 08/07/2012 02:50 PM, Paul Moore wrote:
> > >> On Tue, Aug 7, 2012 at 2:12 PM, John Stultz <[email protected]>
> > >>
> > >> wrote:
> > >>> Hi,
> > >>>
> > >>> With my kvm environment using 3.6-rc1+, I'm seeing NULL pointer
> > >>>
> > >>> dereferences in selinux_ip_postroute_compat(). It looks like the sksec
> > >>> value
> > >>>
> > >>> is null and we die in the following line:
> > >>> if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
> > >>>
> > >>> This triggers every time I shutdown the machine, but has also
> > >>> triggered
> > >>> randomly after a few hours.
> > >>>
> > >>> This is on an ubuntu 12.04 image, not using selinux.
> > >>
> > >> NOTE: Adding the SELinux list to the CC line
> > >
> > > Thanks!
> > >
> > >> Hi,
> > >>
> > >> I'm trying to understand this and I was hoping you could you clarify a
> > >> few things for me:
> > >>
> > >> * Is the panic in the Ubuntu 12.04 guest, or the host? If the host,
> > >> could you share what distribution you are using?
> > >
> > > Sorry, its a 12.04 guest. I think the host is Ubuntu 12.04 as well.
> > >
> > >> * When you say you are not using SELinux, could you be more specific?
> > >> It seems odd that you are not using SELinux but the panic is happening
> > >> in a SELinux hook.
> > >
> > > I just mean that, being Ubuntu, the system (userland) isn't configured
> > > to
> > > use selinux. SELinux is just enabled in the kernel config.
> >
> > Thanks for the quick response, I'll setup an Ubuntu guest and see if I
> > can reproduce this ... something is odd. Anything non-standard about
> > your guest install or anything else you think might be helpful?
>
> The problem seems to be that selinux_nf_ip_init() was called, which
> registers the selinux_ipv4_ops (and ipv6). Those should not get registered
> if selinux ends up not being loaded (as in, if apparmor is loaded first),
> since as you've found here the selinux lsm hooks won't be called to set
> call selinux_sk_alloc_security().
>
> I assume what's happening is that CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE
> was set to 1, but selinux ended up being set to disabled after the
> __initcall(selinux_nf_ip_init) ran? Weird.

Yeah, nothing obvious is jumping out at me in the code except for some weird
race condition like you mention above. I'm downloading an Ubuntu ISO right
now, it should be ready to play with tomorrow morning.

--
paul moore
http://www.paul-moore.com

2012-08-07 22:27:04

by John Stultz

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On 08/07/2012 03:01 PM, Paul Moore wrote:
> On Tue, Aug 7, 2012 at 5:58 PM, John Stultz <[email protected]> wrote:
>> On 08/07/2012 02:50 PM, Paul Moore wrote:
>>> On Tue, Aug 7, 2012 at 2:12 PM, John Stultz <[email protected]>
>>> wrote:
>>>> Hi,
>>>> With my kvm environment using 3.6-rc1+, I'm seeing NULL pointer
>>>> dereferences in selinux_ip_postroute_compat(). It looks like the sksec
>>>> value
>>>> is null and we die in the following line:
>>>>
>>>> if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
>>>>
>>>> This triggers every time I shutdown the machine, but has also triggered
>>>> randomly after a few hours.
>>>>
>>>> This is on an ubuntu 12.04 image, not using selinux.
>>> NOTE: Adding the SELinux list to the CC line
>> Thanks!
>>
>>> Hi,
>>>
>>> I'm trying to understand this and I was hoping you could you clarify a
>>> few things for me:
>>>
>>> * Is the panic in the Ubuntu 12.04 guest, or the host? If the host,
>>> could you share what distribution you are using?
>> Sorry, its a 12.04 guest. I think the host is Ubuntu 12.04 as well.
>>
>>
>>> * When you say you are not using SELinux, could you be more specific?
>>> It seems odd that you are not using SELinux but the panic is happening
>>> in a SELinux hook.
>> I just mean that, being Ubuntu, the system (userland) isn't configured to
>> use selinux. SELinux is just enabled in the kernel config.
> Thanks for the quick response, I'll setup an Ubuntu guest and see if I
> can reproduce this ... something is odd. Anything non-standard about
> your guest install or anything else you think might be helpful?
Don't think so. Just a standard 64bit ubuntu 12.04 install.

Since I'm booting kernel/initrd from the commandline, the initrd *may*
be older then 12.04, I can't quite remember when I copied that out of
the image. I'll see if it still triggers if I copy the current initrd out.

thanks
-john

2012-08-07 23:05:35

by John Stultz

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On 08/07/2012 03:17 PM, Serge E. Hallyn wrote:
> Quoting Paul Moore ([email protected]):
>> On Tue, Aug 7, 2012 at 5:58 PM, John Stultz <[email protected]> wrote:
>>> On 08/07/2012 02:50 PM, Paul Moore wrote:
>>>> On Tue, Aug 7, 2012 at 2:12 PM, John Stultz <[email protected]>
>>>> wrote:
>>>>> Hi,
>>>>> With my kvm environment using 3.6-rc1+, I'm seeing NULL pointer
>>>>> dereferences in selinux_ip_postroute_compat(). It looks like the sksec
>>>>> value
>>>>> is null and we die in the following line:
>>>>>
>>>>> if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
>>>>>
>>>>> This triggers every time I shutdown the machine, but has also triggered
>>>>> randomly after a few hours.
>>>>>
>>>>> This is on an ubuntu 12.04 image, not using selinux.
>>>> NOTE: Adding the SELinux list to the CC line
>>> Thanks!
>>>
>>>> Hi,
>>>>
>>>> I'm trying to understand this and I was hoping you could you clarify a
>>>> few things for me:
>>>>
>>>> * Is the panic in the Ubuntu 12.04 guest, or the host? If the host,
>>>> could you share what distribution you are using?
>>> Sorry, its a 12.04 guest. I think the host is Ubuntu 12.04 as well.
>>>
>>>
>>>> * When you say you are not using SELinux, could you be more specific?
>>>> It seems odd that you are not using SELinux but the panic is happening
>>>> in a SELinux hook.
>>> I just mean that, being Ubuntu, the system (userland) isn't configured to
>>> use selinux. SELinux is just enabled in the kernel config.
>> Thanks for the quick response, I'll setup an Ubuntu guest and see if I
>> can reproduce this ... something is odd. Anything non-standard about
>> your guest install or anything else you think might be helpful?
> The problem seems to be that selinux_nf_ip_init() was called, which
> registers the selinux_ipv4_ops (and ipv6). Those should not get registered
> if selinux ends up not being loaded (as in, if apparmor is loaded first),
> since as you've found here the selinux lsm hooks won't be called to set
> call selinux_sk_alloc_security().
This sounds about right:
root@testvm:~# dmesg | grep SELinux
[ 0.004578] SELinux: Initializing.
[ 0.005704] SELinux: Starting in permissive mode
[ 2.235034] SELinux: Registering netfilter hooks


> I assume what's happening is that CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE was
> set to 1, but selinux ended up being set to disabled after the
> __initcall(selinux_nf_ip_init) ran? Weird.
This looks right as well:

# zcat config.gz | grep SELINUX
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SELINUX_BOOTPARAM=y
CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1
CONFIG_SECURITY_SELINUX_DISABLE=y
CONFIG_SECURITY_SELINUX_DEVELOP=y
CONFIG_SECURITY_SELINUX_AVC_STATS=y
CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1
# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set
CONFIG_DEFAULT_SECURITY_SELINUX=y


Since the problem isn't completely obvious, I'm starting a bisection to
narrow this down some more.

thanks
-john

2012-08-07 23:24:56

by john stultz

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On 08/07/2012 03:26 PM, John Stultz wrote:
> On 08/07/2012 03:01 PM, Paul Moore wrote:
>> On Tue, Aug 7, 2012 at 5:58 PM, John Stultz <[email protected]>
>> wrote:
>>> On 08/07/2012 02:50 PM, Paul Moore wrote:
>>>> On Tue, Aug 7, 2012 at 2:12 PM, John Stultz <[email protected]>
>>>> wrote:
>>>>> Hi,
>>>>> With my kvm environment using 3.6-rc1+, I'm seeing NULL pointer
>>>>> dereferences in selinux_ip_postroute_compat(). It looks like the
>>>>> sksec
>>>>> value
>>>>> is null and we die in the following line:
>>>>>
>>>>> if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
>>>>>
>>>>> This triggers every time I shutdown the machine, but has also
>>>>> triggered
>>>>> randomly after a few hours.
>>>>>
>>>>> This is on an ubuntu 12.04 image, not using selinux.
>>>> NOTE: Adding the SELinux list to the CC line
>>> Thanks!
>>>
>>>> Hi,
>>>>
>>>> I'm trying to understand this and I was hoping you could you clarify a
>>>> few things for me:
>>>>
>>>> * Is the panic in the Ubuntu 12.04 guest, or the host? If the host,
>>>> could you share what distribution you are using?
>>> Sorry, its a 12.04 guest. I think the host is Ubuntu 12.04 as well.
>>>
>>>
>>>> * When you say you are not using SELinux, could you be more specific?
>>>> It seems odd that you are not using SELinux but the panic is happening
>>>> in a SELinux hook.
>>> I just mean that, being Ubuntu, the system (userland) isn't
>>> configured to
>>> use selinux. SELinux is just enabled in the kernel config.
>> Thanks for the quick response, I'll setup an Ubuntu guest and see if I
>> can reproduce this ... something is odd. Anything non-standard about
>> your guest install or anything else you think might be helpful?
> Don't think so. Just a standard 64bit ubuntu 12.04 install.
>
> Since I'm booting kernel/initrd from the commandline, the initrd *may*
> be older then 12.04, I can't quite remember when I copied that out of
> the image. I'll see if it still triggers if I copy the current initrd
> out.

Nope, that's not it, I just triggered the same thing w/ the Ubuntu 12.04
initrd on the image.

thanks
-john

2012-08-08 16:58:45

by John Johansen

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On 08/07/2012 03:17 PM, Serge E. Hallyn wrote:
> Quoting Paul Moore ([email protected]):
>> On Tue, Aug 7, 2012 at 5:58 PM, John Stultz <[email protected]> wrote:
>>> On 08/07/2012 02:50 PM, Paul Moore wrote:
>>>>
>>>> On Tue, Aug 7, 2012 at 2:12 PM, John Stultz <[email protected]>
>>>> wrote:
>>>>>
>>>>> Hi,
>>>>> With my kvm environment using 3.6-rc1+, I'm seeing NULL pointer
>>>>> dereferences in selinux_ip_postroute_compat(). It looks like the sksec
>>>>> value
>>>>> is null and we die in the following line:
>>>>>
>>>>> if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
>>>>>
>>>>> This triggers every time I shutdown the machine, but has also triggered
>>>>> randomly after a few hours.
>>>>>
>>>>> This is on an ubuntu 12.04 image, not using selinux.
>>>>
>>>> NOTE: Adding the SELinux list to the CC line
>>>
>>> Thanks!
>>>
>>>> Hi,
>>>>
>>>> I'm trying to understand this and I was hoping you could you clarify a
>>>> few things for me:
>>>>
>>>> * Is the panic in the Ubuntu 12.04 guest, or the host? If the host,
>>>> could you share what distribution you are using?
>>>
>>> Sorry, its a 12.04 guest. I think the host is Ubuntu 12.04 as well.
>>>
>>>
>>>> * When you say you are not using SELinux, could you be more specific?
>>>> It seems odd that you are not using SELinux but the panic is happening
>>>> in a SELinux hook.
>>>
>>> I just mean that, being Ubuntu, the system (userland) isn't configured to
>>> use selinux. SELinux is just enabled in the kernel config.
>>
>> Thanks for the quick response, I'll setup an Ubuntu guest and see if I
>> can reproduce this ... something is odd. Anything non-standard about
>> your guest install or anything else you think might be helpful?
>
> The problem seems to be that selinux_nf_ip_init() was called, which
> registers the selinux_ipv4_ops (and ipv6). Those should not get registered
> if selinux ends up not being loaded (as in, if apparmor is loaded first),
> since as you've found here the selinux lsm hooks won't be called to set
> call selinux_sk_alloc_security().
>
> I assume what's happening is that CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE was
> set to 1, but selinux ended up being set to disabled after the
> __initcall(selinux_nf_ip_init) ran? Weird.
>
Its not an Ubuntu kernel. The config has selinux set as the only LSM and
it is configured to be on by default

2012-08-08 19:15:33

by john stultz

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On 08/07/2012 03:37 PM, John Stultz wrote:
> On 08/07/2012 03:17 PM, Serge E. Hallyn wrote:
>> Quoting Paul Moore ([email protected]):
>>> On Tue, Aug 7, 2012 at 5:58 PM, John Stultz <[email protected]>
>>> wrote:
>>>> On 08/07/2012 02:50 PM, Paul Moore wrote:
>>>>> On Tue, Aug 7, 2012 at 2:12 PM, John Stultz <[email protected]>
>>>>> wrote:
>>>>>> Hi,
>>>>>> With my kvm environment using 3.6-rc1+, I'm seeing NULL
>>>>>> pointer
>>>>>> dereferences in selinux_ip_postroute_compat(). It looks like the
>>>>>> sksec
>>>>>> value
>>>>>> is null and we die in the following line:
>>>>>>
>>>>>> if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
>>>>>>
>>>>>> This triggers every time I shutdown the machine, but has also
>>>>>> triggered
>>>>>> randomly after a few hours.
[snip]
>> The problem seems to be that selinux_nf_ip_init() was called, which
>> registers the selinux_ipv4_ops (and ipv6). Those should not get
>> registered
>> if selinux ends up not being loaded (as in, if apparmor is loaded
>> first),
>> since as you've found here the selinux lsm hooks won't be called to set
>> call selinux_sk_alloc_security().
> This sounds about right:
> root@testvm:~# dmesg | grep SELinux
> [ 0.004578] SELinux: Initializing.
> [ 0.005704] SELinux: Starting in permissive mode
> [ 2.235034] SELinux: Registering netfilter hooks
>
>> I assume what's happening is that
>> CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE was
>> set to 1, but selinux ended up being set to disabled after the
>> __initcall(selinux_nf_ip_init) ran? Weird.
> This looks right as well:
>
> # zcat config.gz | grep SELINUX
> CONFIG_SECURITY_SELINUX=y
> CONFIG_SECURITY_SELINUX_BOOTPARAM=y
> CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1
> CONFIG_SECURITY_SELINUX_DISABLE=y
> CONFIG_SECURITY_SELINUX_DEVELOP=y
> CONFIG_SECURITY_SELINUX_AVC_STATS=y
> CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1
> # CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set
> CONFIG_DEFAULT_SECURITY_SELINUX=y
>
>
> Since the problem isn't completely obvious, I'm starting a bisection
> to narrow this down some more.

So I bisected this down and it seems to be the following commit:

commit be9f4a44e7d41cee50ddb5f038fc2391cbbb4046
Author: Eric Dumazet <[email protected]>
Date: Thu Jul 19 07:34:03 2012 +0000

ipv4: tcp: remove per net tcp_sock


It doesn't revert totally cleanly, but after fixing up the rejections
and booting with this patch removed on top of Linus' head the oops on
shutdown goes away.

thanks
-john

2012-08-08 19:27:00

by Paul Moore

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Wednesday, August 08, 2012 12:14:42 PM John Stultz wrote:
> So I bisected this down and it seems to be the following commit:
>
> commit be9f4a44e7d41cee50ddb5f038fc2391cbbb4046
> Author: Eric Dumazet <[email protected]>
> Date: Thu Jul 19 07:34:03 2012 +0000
>
> ipv4: tcp: remove per net tcp_sock
>
>
> It doesn't revert totally cleanly, but after fixing up the rejections
> and booting with this patch removed on top of Linus' head the oops on
> shutdown goes away.

Thanks!

It looks the like there is a bug in ip_send_unicast_reply() which uses a
inet_sock/sock struct which does not have the LSM data properly initialized.

I'll put together a patch shortly.

--
paul moore
http://www.paul-moore.com

2012-08-08 19:29:47

by Eric Dumazet

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Wed, 2012-08-08 at 12:14 -0700, John Stultz wrote:
> On 08/07/2012 03:37 PM, John Stultz wrote:
> > On 08/07/2012 03:17 PM, Serge E. Hallyn wrote:
> >> Quoting Paul Moore ([email protected]):
> >>> On Tue, Aug 7, 2012 at 5:58 PM, John Stultz <[email protected]>
> >>> wrote:
> >>>> On 08/07/2012 02:50 PM, Paul Moore wrote:
> >>>>> On Tue, Aug 7, 2012 at 2:12 PM, John Stultz <[email protected]>
> >>>>> wrote:
> >>>>>> Hi,
> >>>>>> With my kvm environment using 3.6-rc1+, I'm seeing NULL
> >>>>>> pointer
> >>>>>> dereferences in selinux_ip_postroute_compat(). It looks like the
> >>>>>> sksec
> >>>>>> value
> >>>>>> is null and we die in the following line:
> >>>>>>
> >>>>>> if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
> >>>>>>
> >>>>>> This triggers every time I shutdown the machine, but has also
> >>>>>> triggered
> >>>>>> randomly after a few hours.
> [snip]
> >> The problem seems to be that selinux_nf_ip_init() was called, which
> >> registers the selinux_ipv4_ops (and ipv6). Those should not get
> >> registered
> >> if selinux ends up not being loaded (as in, if apparmor is loaded
> >> first),
> >> since as you've found here the selinux lsm hooks won't be called to set
> >> call selinux_sk_alloc_security().
> > This sounds about right:
> > root@testvm:~# dmesg | grep SELinux
> > [ 0.004578] SELinux: Initializing.
> > [ 0.005704] SELinux: Starting in permissive mode
> > [ 2.235034] SELinux: Registering netfilter hooks
> >
> >> I assume what's happening is that
> >> CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE was
> >> set to 1, but selinux ended up being set to disabled after the
> >> __initcall(selinux_nf_ip_init) ran? Weird.
> > This looks right as well:
> >
> > # zcat config.gz | grep SELINUX
> > CONFIG_SECURITY_SELINUX=y
> > CONFIG_SECURITY_SELINUX_BOOTPARAM=y
> > CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1
> > CONFIG_SECURITY_SELINUX_DISABLE=y
> > CONFIG_SECURITY_SELINUX_DEVELOP=y
> > CONFIG_SECURITY_SELINUX_AVC_STATS=y
> > CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1
> > # CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set
> > CONFIG_DEFAULT_SECURITY_SELINUX=y
> >
> >
> > Since the problem isn't completely obvious, I'm starting a bisection
> > to narrow this down some more.
>
> So I bisected this down and it seems to be the following commit:
>
> commit be9f4a44e7d41cee50ddb5f038fc2391cbbb4046
> Author: Eric Dumazet <[email protected]>
> Date: Thu Jul 19 07:34:03 2012 +0000
>
> ipv4: tcp: remove per net tcp_sock
>
>
> It doesn't revert totally cleanly, but after fixing up the rejections
> and booting with this patch removed on top of Linus' head the oops on
> shutdown goes away.

Thanks for doing this.

So sk_security is NULL and selinux crashes on it.

I guess I need to call security_sk_alloc().


2012-08-08 19:38:28

by Eric Dumazet

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Wed, 2012-08-08 at 15:26 -0400, Paul Moore wrote:
> On Wednesday, August 08, 2012 12:14:42 PM John Stultz wrote:
> > So I bisected this down and it seems to be the following commit:
> >
> > commit be9f4a44e7d41cee50ddb5f038fc2391cbbb4046
> > Author: Eric Dumazet <[email protected]>
> > Date: Thu Jul 19 07:34:03 2012 +0000
> >
> > ipv4: tcp: remove per net tcp_sock
> >
> >
> > It doesn't revert totally cleanly, but after fixing up the rejections
> > and booting with this patch removed on top of Linus' head the oops on
> > shutdown goes away.
>
> Thanks!
>
> It looks the like there is a bug in ip_send_unicast_reply() which uses a
> inet_sock/sock struct which does not have the LSM data properly initialized.
>
> I'll put together a patch shortly.
>

Something like this ?

diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index ba39a52..027a331 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -1524,6 +1524,10 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
sk->sk_priority = skb->priority;
sk->sk_protocol = ip_hdr(skb)->protocol;
sk->sk_bound_dev_if = arg->bound_dev_if;
+#ifdef CONFIG_SECURITY
+ if (!sk->sk_security && security_sk_alloc(sk, PF_INET, GFP_ATOMIC))
+ goto out;
+#endif
sock_net_set(sk, net);
__skb_queue_head_init(&sk->sk_write_queue);
sk->sk_sndbuf = sysctl_wmem_default;
@@ -1539,7 +1543,7 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
ip_push_pending_frames(sk, &fl4);
}
-
+out:
put_cpu_var(unicast_sock);

ip_rt_put(rt);

2012-08-08 19:50:26

by john stultz

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On 08/08/2012 12:38 PM, Eric Dumazet wrote:
> On Wed, 2012-08-08 at 15:26 -0400, Paul Moore wrote:
>> It looks the like there is a bug in ip_send_unicast_reply() which uses a
>> inet_sock/sock struct which does not have the LSM data properly initialized.
>>
>> I'll put together a patch shortly.
> Something like this ?
>
> diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
> index ba39a52..027a331 100644
> --- a/net/ipv4/ip_output.c
> +++ b/net/ipv4/ip_output.c
> @@ -1524,6 +1524,10 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
> sk->sk_priority = skb->priority;
> sk->sk_protocol = ip_hdr(skb)->protocol;
> sk->sk_bound_dev_if = arg->bound_dev_if;
> +#ifdef CONFIG_SECURITY
> + if (!sk->sk_security && security_sk_alloc(sk, PF_INET, GFP_ATOMIC))
> + goto out;
> +#endif
> sock_net_set(sk, net);
> __skb_queue_head_init(&sk->sk_write_queue);
> sk->sk_sndbuf = sysctl_wmem_default;
> @@ -1539,7 +1543,7 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
> skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
> ip_push_pending_frames(sk, &fl4);
> }
> -
> +out:
> put_cpu_var(unicast_sock);
>
> ip_rt_put(rt);

I can't comment on the patch itself, but I tested it against Linus' HEAD
and it seems to resolve the oops on shutdown for me.

thanks
-john

2012-08-08 19:50:53

by Paul Moore

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Wednesday, August 08, 2012 09:38:21 PM Eric Dumazet wrote:
> On Wed, 2012-08-08 at 15:26 -0400, Paul Moore wrote:
> > On Wednesday, August 08, 2012 12:14:42 PM John Stultz wrote:
> > > So I bisected this down and it seems to be the following commit:
> > >
> > > commit be9f4a44e7d41cee50ddb5f038fc2391cbbb4046
> > > Author: Eric Dumazet <[email protected]>
> > > Date: Thu Jul 19 07:34:03 2012 +0000
> > >
> > > ipv4: tcp: remove per net tcp_sock
> > >
> > > It doesn't revert totally cleanly, but after fixing up the rejections
> > > and booting with this patch removed on top of Linus' head the oops on
> > > shutdown goes away.
> >
> > Thanks!
> >
> > It looks the like there is a bug in ip_send_unicast_reply() which uses a
> > inet_sock/sock struct which does not have the LSM data properly
> > initialized.
> >
> > I'll put together a patch shortly.
>
> Something like this ?

Yep. I was just trying to see if there was a way we could avoid having to
make it conditional on CONFIG_SECURITY, but I think this is a better approach
than the alternatives.

I'm also looking into making sure we get a sane LSM label on the per-cpu sock
as security_sk_alloc() just allocates and initializes the LSM blob to a basic
starting value (unlabeled_t in the case of SELinux) ... that is likely to be
the tricky bit.

Regardless, I'm okay with us merging the patch below now to fix the panic and
I'll supply a follow-up patch to fix the labeling once I figure out a solution
that seems reasonable. Does that work for you? David?

Acked-by: Paul Moore <[email protected]>

> diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
> index ba39a52..027a331 100644
> --- a/net/ipv4/ip_output.c
> +++ b/net/ipv4/ip_output.c
> @@ -1524,6 +1524,10 @@ void ip_send_unicast_reply(struct net *net, struct
> sk_buff *skb, __be32 daddr, sk->sk_priority = skb->priority;
> sk->sk_protocol = ip_hdr(skb)->protocol;
> sk->sk_bound_dev_if = arg->bound_dev_if;
> +#ifdef CONFIG_SECURITY
> + if (!sk->sk_security && security_sk_alloc(sk, PF_INET, GFP_ATOMIC))
> + goto out;
> +#endif
> sock_net_set(sk, net);
> __skb_queue_head_init(&sk->sk_write_queue);
> sk->sk_sndbuf = sysctl_wmem_default;
> @@ -1539,7 +1543,7 @@ void ip_send_unicast_reply(struct net *net, struct
> sk_buff *skb, __be32 daddr, skb_set_queue_mapping(nskb,
> skb_get_queue_mapping(skb));
> ip_push_pending_frames(sk, &fl4);
> }
> -
> +out:
> put_cpu_var(unicast_sock);
>
> ip_rt_put(rt);

--
paul moore
http://www.paul-moore.com

2012-08-08 19:59:34

by Eric Paris

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Wed, Aug 8, 2012 at 3:38 PM, Eric Dumazet <[email protected]> wrote:
> On Wed, 2012-08-08 at 15:26 -0400, Paul Moore wrote:

> diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
> index ba39a52..027a331 100644
> --- a/net/ipv4/ip_output.c
> +++ b/net/ipv4/ip_output.c
> @@ -1524,6 +1524,10 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
> sk->sk_priority = skb->priority;
> sk->sk_protocol = ip_hdr(skb)->protocol;
> sk->sk_bound_dev_if = arg->bound_dev_if;
> +#ifdef CONFIG_SECURITY
> + if (!sk->sk_security && security_sk_alloc(sk, PF_INET, GFP_ATOMIC))
> + goto out;
> +#endif
> sock_net_set(sk, net);
> __skb_queue_head_init(&sk->sk_write_queue);
> sk->sk_sndbuf = sysctl_wmem_default;
> @@ -1539,7 +1543,7 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
> skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
> ip_push_pending_frames(sk, &fl4);
> }
> -
> +out:
> put_cpu_var(unicast_sock);
>
> ip_rt_put(rt);

Seems wrong. We shouldn't ever need ifdef CONFIG_SECURITY in core
code. Ifndef CONF_SECURITY then security_sk_alloc() is a static
inline return 0; I guess the question is "Where did the sk come
from"? Why wasn't security_sk_alloc() called when it was allocated?
Should it have been updated at some time and that wasn't done either?
Seems wrong to be putting packets on the queue for a socket where the
security data was never allocated and was never set to its proper
state.

there must be a bigger bug here...

-Eric

2012-08-08 20:04:10

by Eric Dumazet

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Wed, 2012-08-08 at 15:50 -0400, Paul Moore wrote:

> Yep. I was just trying to see if there was a way we could avoid having to
> make it conditional on CONFIG_SECURITY, but I think this is a better approach
> than the alternatives.
>
> I'm also looking into making sure we get a sane LSM label on the per-cpu sock
> as security_sk_alloc() just allocates and initializes the LSM blob to a basic
> starting value (unlabeled_t in the case of SELinux) ... that is likely to be
> the tricky bit.

It seems previous code did the same thing in sk_prot_alloc() ?


>
> Regardless, I'm okay with us merging the patch below now to fix the panic and
> I'll supply a follow-up patch to fix the labeling once I figure out a solution
> that seems reasonable. Does that work for you? David?
>
> Acked-by: Paul Moore <[email protected]>

John, could you confirm this fixes the problem ?

2012-08-08 20:04:58

by Eric Dumazet

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Wed, 2012-08-08 at 12:49 -0700, John Stultz wrote:

> I can't comment on the patch itself, but I tested it against Linus' HEAD
> and it seems to resolve the oops on shutdown for me.

OK, thanks !


2012-08-08 20:09:45

by Eric Dumazet

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Wed, 2012-08-08 at 15:59 -0400, Eric Paris wrote:

> Seems wrong. We shouldn't ever need ifdef CONFIG_SECURITY in core
> code.

Sure but it seems include file misses an accessor for this.

We could add it on a future cleanup patch, as Paul mentioned.

> Ifndef CONF_SECURITY then security_sk_alloc() is a static
> inline return 0; I guess the question is "Where did the sk come
> from"? Why wasn't security_sk_alloc() called when it was allocated?
> Should it have been updated at some time and that wasn't done either?
> Seems wrong to be putting packets on the queue for a socket where the
> security data was never allocated and was never set to its proper
> state.
>

IMHO it seems wrong to even care about security for internal sockets.

They are per cpu, shared for all users on the machine.

What kind of security do you envision exactly ?


These unicast_sock are percpu, and preallocated.

/*
* Generic function to send a packet as reply to another packet.
* Used to send some TCP resets/acks so far.
*
* Use a fake percpu inet socket to avoid false sharing and contention.
*/
static DEFINE_PER_CPU(struct inet_sock, unicast_sock) = {
.sk = {
.__sk_common = {
.skc_refcnt = ATOMIC_INIT(1),
},
.sk_wmem_alloc = ATOMIC_INIT(1),
.sk_allocation = GFP_ATOMIC,
.sk_flags = (1UL << SOCK_USE_WRITE_QUEUE),
},
.pmtudisc = IP_PMTUDISC_WANT,
.uc_ttl = -1,
};

2012-08-08 20:33:06

by Eric Dumazet

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Wed, 2012-08-08 at 22:09 +0200, Eric Dumazet wrote:
> On Wed, 2012-08-08 at 15:59 -0400, Eric Paris wrote:
>
> > Seems wrong. We shouldn't ever need ifdef CONFIG_SECURITY in core
> > code.
>
> Sure but it seems include file misses an accessor for this.
>
> We could add it on a future cleanup patch, as Paul mentioned.

I cooked following patch.
But smack/smack_lsm.c makes a reference to
smk_of_current()... so it seems we are in a hole...

It makes little sense to me to have any kind of security on this
internal sockets.

Maybe selinux should not crash if sk->sk_security is NULL ?



include/linux/security.h | 6 +++---
net/core/sock.c | 2 +-
net/ipv4/ip_output.c | 4 +++-
security/security.c | 4 ++--
security/selinux/hooks.c | 5 ++++-
security/smack/smack_lsm.c | 5 ++++-
6 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/include/linux/security.h b/include/linux/security.h
index 4e5a73c..aa648b2 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1601,7 +1601,7 @@ struct security_operations {
int (*socket_sock_rcv_skb) (struct sock *sk, struct sk_buff *skb);
int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len);
int (*socket_getpeersec_dgram) (struct socket *sock, struct sk_buff *skb, u32 *secid);
- int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority);
+ int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority, bool check);
void (*sk_free_security) (struct sock *sk);
void (*sk_clone_security) (const struct sock *sk, struct sock *newsk);
void (*sk_getsecid) (struct sock *sk, u32 *secid);
@@ -2539,7 +2539,7 @@ int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb);
int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
int __user *optlen, unsigned len);
int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid);
-int security_sk_alloc(struct sock *sk, int family, gfp_t priority);
+int security_sk_alloc(struct sock *sk, int family, gfp_t priority, bool check);
void security_sk_free(struct sock *sk);
void security_sk_clone(const struct sock *sk, struct sock *newsk);
void security_sk_classify_flow(struct sock *sk, struct flowi *fl);
@@ -2667,7 +2667,7 @@ static inline int security_socket_getpeersec_dgram(struct socket *sock, struct s
return -ENOPROTOOPT;
}

-static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
+static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority, bool check)
{
return 0;
}
diff --git a/net/core/sock.c b/net/core/sock.c
index 8f67ced..e00cadf 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1186,7 +1186,7 @@ static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,
if (sk != NULL) {
kmemcheck_annotate_bitfield(sk, flags);

- if (security_sk_alloc(sk, family, priority))
+ if (security_sk_alloc(sk, family, priority, false))
goto out_free;

if (!try_module_get(prot->owner))
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 76dde25..b233d6e 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -1524,6 +1524,8 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
sk->sk_priority = skb->priority;
sk->sk_protocol = ip_hdr(skb)->protocol;
sk->sk_bound_dev_if = arg->bound_dev_if;
+ if (security_sk_alloc(sk, PF_INET, GFP_ATOMIC, true))
+ goto out;
sock_net_set(sk, net);
__skb_queue_head_init(&sk->sk_write_queue);
sk->sk_sndbuf = sysctl_wmem_default;
@@ -1539,7 +1541,7 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
ip_push_pending_frames(sk, &fl4);
}
-
+out:
put_cpu_var(unicast_sock);

ip_rt_put(rt);
diff --git a/security/security.c b/security/security.c
index 860aeb3..af7404e 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1146,9 +1146,9 @@ int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u
}
EXPORT_SYMBOL(security_socket_getpeersec_dgram);

-int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
+int security_sk_alloc(struct sock *sk, int family, gfp_t priority, bool check)
{
- return security_ops->sk_alloc_security(sk, family, priority);
+ return security_ops->sk_alloc_security(sk, family, priority, check);
}

void security_sk_free(struct sock *sk)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 6c77f63..459eca6 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -4289,10 +4289,13 @@ out:
return 0;
}

-static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
+static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority, bool check)
{
struct sk_security_struct *sksec;

+ if (check && sk->sk_security)
+ return 0;
+
sksec = kzalloc(sizeof(*sksec), priority);
if (!sksec)
return -ENOMEM;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 8221514..8965cf1 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1754,11 +1754,14 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
*
* Returns 0 on success, -ENOMEM is there's no memory
*/
-static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
+static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags, bool check)
{
char *csp = smk_of_current();
struct socket_smack *ssp;

+ if (check && sk->sk_security)
+ return 0;
+
ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
if (ssp == NULL)
return -ENOMEM;


2012-08-08 20:35:14

by Paul Moore

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Wednesday, August 08, 2012 10:09:38 PM Eric Dumazet wrote:
> On Wed, 2012-08-08 at 15:59 -0400, Eric Paris wrote:
> > Seems wrong. We shouldn't ever need ifdef CONFIG_SECURITY in core
> > code.
>
> Sure but it seems include file misses an accessor for this.
>
> We could add it on a future cleanup patch, as Paul mentioned.

Actually, the issue is that the shared socket doesn't have an init/alloc
function to do the LSM allocation like we do with other sockets so Eric's
patch does it as part of ip_send_unicast_reply().

If we look at the relevant part of Eric's patch:

+#ifdef CONFIG_SECURITY
+ if (!sk->sk_security && security_sk_alloc(sk, PF_INET, GFP_ATOMIC))
+ goto out;
+#endif

... if we were to remove the CONFIG_SECURITY conditional we would end up
calling security_sk_alloc() each time through in the CONFIG_SECURITY=n case as
sk->sk_security would never be initialized to a non-NULL value. In the
CONFIG_SECURITY=y case it should only be called once as security_sk_alloc()
should set sk->sk_security to a LSM blob.

> > Ifndef CONF_SECURITY then security_sk_alloc() is a static
> >
> > inline return 0; I guess the question is "Where did the sk come
> > from"? Why wasn't security_sk_alloc() called when it was allocated?
> > Should it have been updated at some time and that wasn't done either?
> > Seems wrong to be putting packets on the queue for a socket where the
> > security data was never allocated and was never set to its proper
> > state.
>
> IMHO it seems wrong to even care about security for internal sockets.
>
> They are per cpu, shared for all users on the machine.

The issue, from a security point of view, is that these sockets are sending
network traffic; even if it is just resets and timewait ACKs, it is still
network traffic and the LSMs need to be able to enforce security policy on
this traffic. After all, what would you say if your firewall let these same
packets pass without any filtering?

The issue I'm struggling with at present is how should we handle this traffic
from a LSM perspective. The label based LSMs, e.g. SELinux and Smack, use the
LSM blob assigned to locally generated outbound traffic to identify the
traffic and apply the security policy, so not only do we have to resolve the
issue of ensuring the traffic is labeled correctly, we have to do it with a
shared socket (although the patch didn't change the shared nature of the
socket).

For those who are interested, I think the reasonable labeling solution here is
to go with SECINITSID_KERNEL/kernel_t for SELinux and likely the ambient label
for Smack as in both the TCP reset and timewait ACK there shouldn't be any
actual user data present.

--
paul moore
http://www.paul-moore.com

2012-08-08 20:47:08

by Paul Moore

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Wednesday, August 08, 2012 10:32:52 PM Eric Dumazet wrote:
> On Wed, 2012-08-08 at 22:09 +0200, Eric Dumazet wrote:
> > On Wed, 2012-08-08 at 15:59 -0400, Eric Paris wrote:
> > > Seems wrong. We shouldn't ever need ifdef CONFIG_SECURITY in core
> > > code.
> >
> > Sure but it seems include file misses an accessor for this.
> >
> > We could add it on a future cleanup patch, as Paul mentioned.
>
> I cooked following patch.
> But smack/smack_lsm.c makes a reference to
> smk_of_current()... so it seems we are in a hole...
>
> It makes little sense to me to have any kind of security on this
> internal sockets.
>
> Maybe selinux should not crash if sk->sk_security is NULL ?

I realize our last emails probably passed each other mid-flight, but hopefully
it explains why we can't just pass packets when sk->sk_security is NULL.

Regardless, some quick comments below ...

> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 6c77f63..459eca6 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -4289,10 +4289,13 @@ out:
> return 0;
> }
>
> -static int selinux_sk_alloc_security(struct sock *sk, int family, ...
> +static int selinux_sk_alloc_security(struct sock *sk, int family, ...
> {
> struct sk_security_struct *sksec;
>
> + if (check && sk->sk_security)
> + return 0;
> +
> sksec = kzalloc(sizeof(*sksec), priority);
> if (!sksec)
> return -ENOMEM;

I think I might replace the "check" boolean with a "kern/kernel" boolean so
that in addition to the allocation we can also initialize the socket to
SECINITSID_KERNEL/kernel_t here in the case when the boolean is set. The only
place that would set the boolean to true would be ip_send_unicast_reply(), all
other callers would set it to false.

> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index 8221514..8965cf1 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -1754,11 +1754,14 @@ static void smack_task_to_inode(struct task_struct
> *p, struct inode *inode) *
> * Returns 0 on success, -ENOMEM is there's no memory
> */
> -static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t
> gfp_flags) +static int smack_sk_alloc_security(struct sock *sk, int family,
> gfp_t gfp_flags, bool check) {
> char *csp = smk_of_current();
> struct socket_smack *ssp;
>
> + if (check && sk->sk_security)
> + return 0;
> +
> ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
> if (ssp == NULL)
> return -ENOMEM;

In the case of Smack, when the kernel boolean is true I think the right
solution is to use smack_net_ambient.

--
paul moore
http://www.paul-moore.com

2012-08-08 20:51:58

by Eric Paris

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Wed, Aug 8, 2012 at 4:35 PM, Paul Moore <[email protected]> wrote:
> On Wednesday, August 08, 2012 10:09:38 PM Eric Dumazet wrote:

> Actually, the issue is that the shared socket doesn't have an init/alloc
> function to do the LSM allocation like we do with other sockets so Eric's
> patch does it as part of ip_send_unicast_reply().
>
> If we look at the relevant part of Eric's patch:
>
> +#ifdef CONFIG_SECURITY
> + if (!sk->sk_security && security_sk_alloc(sk, PF_INET, GFP_ATOMIC))
> + goto out;
> +#endif
>
> ... if we were to remove the CONFIG_SECURITY conditional we would end up
> calling security_sk_alloc() each time through in the CONFIG_SECURITY=n case as
> sk->sk_security would never be initialized to a non-NULL value. In the
> CONFIG_SECURITY=y case it should only be called once as security_sk_alloc()
> should set sk->sk_security to a LSM blob.

Ifndef SECURITY this turns into (because security_sk_alloc is a static
inline in that case)

if (!sk->sk_security && 0)
goto out;

Which I'd hope the compiler would optimize. So that only leaves us
caring about the case there CONFIG_SECURITY is true. In that case if
we need code which does if !alloc'd then alloc it seems we broke the
model of everything else in the code and added a branch needlessly.

Could we add a __init function which does the security_sk_alloc() in
the same file where we declared them?

>> IMHO it seems wrong to even care about security for internal sockets.
>>
>> They are per cpu, shared for all users on the machine.
>
> The issue, from a security point of view, is that these sockets are sending
> network traffic; even if it is just resets and timewait ACKs, it is still
> network traffic and the LSMs need to be able to enforce security policy on
> this traffic. After all, what would you say if your firewall let these same
> packets pass without any filtering?
>
> The issue I'm struggling with at present is how should we handle this traffic
> from a LSM perspective. The label based LSMs, e.g. SELinux and Smack, use the
> LSM blob assigned to locally generated outbound traffic to identify the
> traffic and apply the security policy, so not only do we have to resolve the
> issue of ensuring the traffic is labeled correctly, we have to do it with a
> shared socket (although the patch didn't change the shared nature of the
> socket).
>
> For those who are interested, I think the reasonable labeling solution here is
> to go with SECINITSID_KERNEL/kernel_t for SELinux and likely the ambient label
> for Smack as in both the TCP reset and timewait ACK there shouldn't be any
> actual user data present.

I'm willing to accept that argument from an SELinux perspective. I'd
also accept the argument that it is private and do something similar
to what we do with IS_PRIVATE on inodes. Although sockets probably
don't have a good field to use...

2012-08-08 21:03:24

by Paul Moore

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Wednesday, August 08, 2012 04:51:56 PM Eric Paris wrote:
> On Wed, Aug 8, 2012 at 4:35 PM, Paul Moore <[email protected]> wrote:
> > On Wednesday, August 08, 2012 10:09:38 PM Eric Dumazet wrote:
> >
> > Actually, the issue is that the shared socket doesn't have an init/alloc
> > function to do the LSM allocation like we do with other sockets so Eric's
> > patch does it as part of ip_send_unicast_reply().
> >
> > If we look at the relevant part of Eric's patch:
> > +#ifdef CONFIG_SECURITY
> > + if (!sk->sk_security && security_sk_alloc(sk, PF_INET,
> > GFP_ATOMIC))
> > + goto out;
> > +#endif
> >
> > ... if we were to remove the CONFIG_SECURITY conditional we would end up
> > calling security_sk_alloc() each time through in the CONFIG_SECURITY=n
> > case as sk->sk_security would never be initialized to a non-NULL value.
> > In the CONFIG_SECURITY=y case it should only be called once as
> > security_sk_alloc() should set sk->sk_security to a LSM blob.
>
> Ifndef SECURITY this turns into (because security_sk_alloc is a static
> inline in that case)
>
> if (!sk->sk_security && 0)
> goto out;
>
> Which I'd hope the compiler would optimize. So that only leaves us
> caring about the case there CONFIG_SECURITY is true. In that case if
> we need code which does if !alloc'd then alloc it seems we broke the
> model of everything else in the code and added a branch needlessly.
>
> Could we add a __init function which does the security_sk_alloc() in
> the same file where we declared them?

Is it safe to call security_sk_alloc() from inside another __init function? I
think in both the case of SELinux and Smack it shouldn't be a problem, but I'm
concerned about the more general case of calling a LSM hook potentially before
the LSM has been initialized.

If that isn't an issue we could probably do something in ip_init().

> > The issue I'm struggling with at present is how should we handle this
> > traffic from a LSM perspective. The label based LSMs, e.g. SELinux and
> > Smack, use the LSM blob assigned to locally generated outbound traffic to
> > identify the traffic and apply the security policy, so not only do we
> > have to resolve the issue of ensuring the traffic is labeled correctly,
> > we have to do it with a shared socket (although the patch didn't change
> > the shared nature of the socket).
> >
> > For those who are interested, I think the reasonable labeling solution
> > here is to go with SECINITSID_KERNEL/kernel_t for SELinux and likely the
> > ambient label for Smack as in both the TCP reset and timewait ACK there
> > shouldn't be any actual user data present.
>
> I'm willing to accept that argument from an SELinux perspective. I'd
> also accept the argument that it is private and do something similar
> to what we do with IS_PRIVATE on inodes. Although sockets probably
> don't have a good field to use...

I'm not aware of one. See my comments on Eric's last patch posting (the other
Eric, not you).

--
paul moore
http://www.paul-moore.com

2012-08-08 21:09:22

by Eric Paris

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Wed, Aug 8, 2012 at 5:03 PM, Paul Moore <[email protected]> wrote:
> On Wednesday, August 08, 2012 04:51:56 PM Eric Paris wrote:

>> Could we add a __init function which does the security_sk_alloc() in
>> the same file where we declared them?
>
> Is it safe to call security_sk_alloc() from inside another __init function? I
> think in both the case of SELinux and Smack it shouldn't be a problem, but I'm
> concerned about the more general case of calling a LSM hook potentially before
> the LSM has been initialized.
>
> If that isn't an issue we could probably do something in ip_init().

The security_initcall() functions should happen way before __init
functions. If an LSM busts, it's the LSM initializing itself too late
not the code here being wrong...

2012-08-08 21:54:55

by Eric Dumazet

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Wed, 2012-08-08 at 16:46 -0400, Paul Moore wrote:
> On Wednesday, August 08, 2012 10:32:52 PM Eric Dumazet wrote:
> > On Wed, 2012-08-08 at 22:09 +0200, Eric Dumazet wrote:
> > > On Wed, 2012-08-08 at 15:59 -0400, Eric Paris wrote:
> > > > Seems wrong. We shouldn't ever need ifdef CONFIG_SECURITY in core
> > > > code.
> > >
> > > Sure but it seems include file misses an accessor for this.
> > >
> > > We could add it on a future cleanup patch, as Paul mentioned.
> >
> > I cooked following patch.
> > But smack/smack_lsm.c makes a reference to
> > smk_of_current()... so it seems we are in a hole...
> >
> > It makes little sense to me to have any kind of security on this
> > internal sockets.
> >
> > Maybe selinux should not crash if sk->sk_security is NULL ?
>
> I realize our last emails probably passed each other mid-flight, but hopefully
> it explains why we can't just pass packets when sk->sk_security is NULL.
>
> Regardless, some quick comments below ...
>
> > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > index 6c77f63..459eca6 100644
> > --- a/security/selinux/hooks.c
> > +++ b/security/selinux/hooks.c
> > @@ -4289,10 +4289,13 @@ out:
> > return 0;
> > }
> >
> > -static int selinux_sk_alloc_security(struct sock *sk, int family, ...
> > +static int selinux_sk_alloc_security(struct sock *sk, int family, ...
> > {
> > struct sk_security_struct *sksec;
> >
> > + if (check && sk->sk_security)
> > + return 0;
> > +
> > sksec = kzalloc(sizeof(*sksec), priority);
> > if (!sksec)
> > return -ENOMEM;
>
> I think I might replace the "check" boolean with a "kern/kernel" boolean so
> that in addition to the allocation we can also initialize the socket to
> SECINITSID_KERNEL/kernel_t here in the case when the boolean is set. The only
> place that would set the boolean to true would be ip_send_unicast_reply(), all
> other callers would set it to false.
>
> > diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> > index 8221514..8965cf1 100644
> > --- a/security/smack/smack_lsm.c
> > +++ b/security/smack/smack_lsm.c
> > @@ -1754,11 +1754,14 @@ static void smack_task_to_inode(struct task_struct
> > *p, struct inode *inode) *
> > * Returns 0 on success, -ENOMEM is there's no memory
> > */
> > -static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t
> > gfp_flags) +static int smack_sk_alloc_security(struct sock *sk, int family,
> > gfp_t gfp_flags, bool check) {
> > char *csp = smk_of_current();
> > struct socket_smack *ssp;
> >
> > + if (check && sk->sk_security)
> > + return 0;
> > +
> > ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
> > if (ssp == NULL)
> > return -ENOMEM;
>
> In the case of Smack, when the kernel boolean is true I think the right
> solution is to use smack_net_ambient.
>

cool, here the last version :

diff --git a/include/linux/security.h b/include/linux/security.h
index 4e5a73c..4d8e454 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1601,7 +1601,7 @@ struct security_operations {
int (*socket_sock_rcv_skb) (struct sock *sk, struct sk_buff *skb);
int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len);
int (*socket_getpeersec_dgram) (struct socket *sock, struct sk_buff *skb, u32 *secid);
- int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority);
+ int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority, bool kernel);
void (*sk_free_security) (struct sock *sk);
void (*sk_clone_security) (const struct sock *sk, struct sock *newsk);
void (*sk_getsecid) (struct sock *sk, u32 *secid);
@@ -2539,7 +2539,7 @@ int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb);
int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
int __user *optlen, unsigned len);
int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid);
-int security_sk_alloc(struct sock *sk, int family, gfp_t priority);
+int security_sk_alloc(struct sock *sk, int family, gfp_t priority, bool kernel);
void security_sk_free(struct sock *sk);
void security_sk_clone(const struct sock *sk, struct sock *newsk);
void security_sk_classify_flow(struct sock *sk, struct flowi *fl);
@@ -2667,7 +2667,7 @@ static inline int security_socket_getpeersec_dgram(struct socket *sock, struct s
return -ENOPROTOOPT;
}

-static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
+static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority, bool kernel)
{
return 0;
}
diff --git a/net/core/sock.c b/net/core/sock.c
index 8f67ced..e00cadf 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1186,7 +1186,7 @@ static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,
if (sk != NULL) {
kmemcheck_annotate_bitfield(sk, flags);

- if (security_sk_alloc(sk, family, priority))
+ if (security_sk_alloc(sk, family, priority, false))
goto out_free;

if (!try_module_get(prot->owner))
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 76dde25..b233d6e 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -1524,6 +1524,8 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
sk->sk_priority = skb->priority;
sk->sk_protocol = ip_hdr(skb)->protocol;
sk->sk_bound_dev_if = arg->bound_dev_if;
+ if (security_sk_alloc(sk, PF_INET, GFP_ATOMIC, true))
+ goto out;
sock_net_set(sk, net);
__skb_queue_head_init(&sk->sk_write_queue);
sk->sk_sndbuf = sysctl_wmem_default;
@@ -1539,7 +1541,7 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
ip_push_pending_frames(sk, &fl4);
}
-
+out:
put_cpu_var(unicast_sock);

ip_rt_put(rt);
diff --git a/security/security.c b/security/security.c
index 860aeb3..23cf297 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1146,9 +1146,9 @@ int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u
}
EXPORT_SYMBOL(security_socket_getpeersec_dgram);

-int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
+int security_sk_alloc(struct sock *sk, int family, gfp_t priority, bool kernel)
{
- return security_ops->sk_alloc_security(sk, family, priority);
+ return security_ops->sk_alloc_security(sk, family, priority, kernel);
}

void security_sk_free(struct sock *sk)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 6c77f63..ccd4374 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -4289,10 +4289,13 @@ out:
return 0;
}

-static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
+static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority, bool kernel)
{
struct sk_security_struct *sksec;

+ if (kernel && sk->sk_security)
+ return 0;
+
sksec = kzalloc(sizeof(*sksec), priority);
if (!sksec)
return -ENOMEM;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 8221514..207d9cc 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1749,20 +1749,25 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
* @sk: the socket
* @family: unused
* @gfp_flags: memory allocation flags
+ * @kernel: true if we should check sk_security being already set
*
* Assign Smack pointers to current
*
* Returns 0 on success, -ENOMEM is there's no memory
*/
-static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
+static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags, bool kernel)
{
char *csp = smk_of_current();
struct socket_smack *ssp;

+ if (kernel && sk->sk_security)
+ return 0;
+
ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
if (ssp == NULL)
return -ENOMEM;
-
+ /* kernel is true if called from ip_send_unicast_reply() */
+ csp = kernel ? smack_net_ambient : smk_of_current();
ssp->smk_in = csp;
ssp->smk_out = csp;
ssp->smk_packet = NULL;

2012-08-09 00:00:31

by Casey Schaufler

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On 8/8/2012 2:54 PM, Eric Dumazet wrote:

By the way, once this proved to be an issue that involved
more than just SELinux it needed to go onto the LSM list as
well.

> On Wed, 2012-08-08 at 16:46 -0400, Paul Moore wrote:
>> On Wednesday, August 08, 2012 10:32:52 PM Eric Dumazet wrote:
>>> On Wed, 2012-08-08 at 22:09 +0200, Eric Dumazet wrote:
>>>> On Wed, 2012-08-08 at 15:59 -0400, Eric Paris wrote:
>>>>> Seems wrong. We shouldn't ever need ifdef CONFIG_SECURITY in core
>>>>> code.
>>>> Sure but it seems include file misses an accessor for this.
>>>>
>>>> We could add it on a future cleanup patch, as Paul mentioned.
>>> I cooked following patch.
>>> But smack/smack_lsm.c makes a reference to
>>> smk_of_current()... so it seems we are in a hole...
>>>
>>> It makes little sense to me to have any kind of security on this
>>> internal sockets.
>>>
>>> Maybe selinux should not crash if sk->sk_security is NULL ?
>> I realize our last emails probably passed each other mid-flight, but hopefully
>> it explains why we can't just pass packets when sk->sk_security is NULL.
>>
>> Regardless, some quick comments below ...
>>
>>> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
>>> index 6c77f63..459eca6 100644
>>> --- a/security/selinux/hooks.c
>>> +++ b/security/selinux/hooks.c
>>> @@ -4289,10 +4289,13 @@ out:
>>> return 0;
>>> }
>>>
>>> -static int selinux_sk_alloc_security(struct sock *sk, int family, ...
>>> +static int selinux_sk_alloc_security(struct sock *sk, int family, ...
>>> {
>>> struct sk_security_struct *sksec;
>>>
>>> + if (check && sk->sk_security)
>>> + return 0;
>>> +
>>> sksec = kzalloc(sizeof(*sksec), priority);
>>> if (!sksec)
>>> return -ENOMEM;
>> I think I might replace the "check" boolean with a "kern/kernel" boolean so
>> that in addition to the allocation we can also initialize the socket to
>> SECINITSID_KERNEL/kernel_t here in the case when the boolean is set. The only
>> place that would set the boolean to true would be ip_send_unicast_reply(), all
>> other callers would set it to false.
>>
>>> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
>>> index 8221514..8965cf1 100644
>>> --- a/security/smack/smack_lsm.c
>>> +++ b/security/smack/smack_lsm.c
>>> @@ -1754,11 +1754,14 @@ static void smack_task_to_inode(struct task_struct
>>> *p, struct inode *inode) *
>>> * Returns 0 on success, -ENOMEM is there's no memory
>>> */
>>> -static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t
>>> gfp_flags) +static int smack_sk_alloc_security(struct sock *sk, int family,
>>> gfp_t gfp_flags, bool check) {
>>> char *csp = smk_of_current();
>>> struct socket_smack *ssp;
>>>
>>> + if (check && sk->sk_security)
>>> + return 0;
>>> +
>>> ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
>>> if (ssp == NULL)
>>> return -ENOMEM;
>> In the case of Smack, when the kernel boolean is true I think the right
>> solution is to use smack_net_ambient.

I confess that my understanding of unicast is limited.
If the intention is to send an unlabeled packet then
indeed smack_net_ambient is the way to go.

>>
> cool, here the last version :
>
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 4e5a73c..4d8e454 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -1601,7 +1601,7 @@ struct security_operations {
> int (*socket_sock_rcv_skb) (struct sock *sk, struct sk_buff *skb);
> int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len);
> int (*socket_getpeersec_dgram) (struct socket *sock, struct sk_buff *skb, u32 *secid);
> - int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority);
> + int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority, bool kernel);

Is there no information already available in the sock
that will tell us this is a unicast operation?

> void (*sk_free_security) (struct sock *sk);
> void (*sk_clone_security) (const struct sock *sk, struct sock *newsk);
> void (*sk_getsecid) (struct sock *sk, u32 *secid);
> @@ -2539,7 +2539,7 @@ int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb);
> int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
> int __user *optlen, unsigned len);
> int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid);
> -int security_sk_alloc(struct sock *sk, int family, gfp_t priority);
> +int security_sk_alloc(struct sock *sk, int family, gfp_t priority, bool kernel);
> void security_sk_free(struct sock *sk);
> void security_sk_clone(const struct sock *sk, struct sock *newsk);
> void security_sk_classify_flow(struct sock *sk, struct flowi *fl);
> @@ -2667,7 +2667,7 @@ static inline int security_socket_getpeersec_dgram(struct socket *sock, struct s
> return -ENOPROTOOPT;
> }
>
> -static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
> +static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority, bool kernel)
> {
> return 0;
> }
> diff --git a/net/core/sock.c b/net/core/sock.c
> index 8f67ced..e00cadf 100644
> --- a/net/core/sock.c
> +++ b/net/core/sock.c
> @@ -1186,7 +1186,7 @@ static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,
> if (sk != NULL) {
> kmemcheck_annotate_bitfield(sk, flags);
>
> - if (security_sk_alloc(sk, family, priority))
> + if (security_sk_alloc(sk, family, priority, false))
> goto out_free;
>
> if (!try_module_get(prot->owner))
> diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
> index 76dde25..b233d6e 100644
> --- a/net/ipv4/ip_output.c
> +++ b/net/ipv4/ip_output.c
> @@ -1524,6 +1524,8 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
> sk->sk_priority = skb->priority;
> sk->sk_protocol = ip_hdr(skb)->protocol;
> sk->sk_bound_dev_if = arg->bound_dev_if;
> + if (security_sk_alloc(sk, PF_INET, GFP_ATOMIC, true))
> + goto out;
> sock_net_set(sk, net);
> __skb_queue_head_init(&sk->sk_write_queue);
> sk->sk_sndbuf = sysctl_wmem_default;
> @@ -1539,7 +1541,7 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
> skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
> ip_push_pending_frames(sk, &fl4);
> }
> -
> +out:
> put_cpu_var(unicast_sock);
>
> ip_rt_put(rt);
> diff --git a/security/security.c b/security/security.c
> index 860aeb3..23cf297 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -1146,9 +1146,9 @@ int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u
> }
> EXPORT_SYMBOL(security_socket_getpeersec_dgram);
>
> -int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
> +int security_sk_alloc(struct sock *sk, int family, gfp_t priority, bool kernel)
> {
> - return security_ops->sk_alloc_security(sk, family, priority);
> + return security_ops->sk_alloc_security(sk, family, priority, kernel);
> }
>
> void security_sk_free(struct sock *sk)
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 6c77f63..ccd4374 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -4289,10 +4289,13 @@ out:
> return 0;
> }
>
> -static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
> +static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority, bool kernel)
> {
> struct sk_security_struct *sksec;
>
> + if (kernel && sk->sk_security)
> + return 0;
> +
> sksec = kzalloc(sizeof(*sksec), priority);
> if (!sksec)
> return -ENOMEM;
> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index 8221514..207d9cc 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -1749,20 +1749,25 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
> * @sk: the socket
> * @family: unused
> * @gfp_flags: memory allocation flags
> + * @kernel: true if we should check sk_security being already set
> *
> * Assign Smack pointers to current
> *
> * Returns 0 on success, -ENOMEM is there's no memory
> */
> -static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
> +static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags, bool kernel)
> {
> char *csp = smk_of_current();
> struct socket_smack *ssp;
>
> + if (kernel && sk->sk_security)
> + return 0;
> +
> ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
> if (ssp == NULL)
> return -ENOMEM;
> -
> + /* kernel is true if called from ip_send_unicast_reply() */
> + csp = kernel ? smack_net_ambient : smk_of_current();

How about ...

if (kernel)
csp = smack_net_ambient;

... as csp is set to smk_of_current() in the declaration.
That, or change the declaration.

> ssp->smk_in = csp;
> ssp->smk_out = csp;
> ssp->smk_packet = NULL;
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
>

2012-08-09 13:30:50

by Paul Moore

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Wednesday, August 08, 2012 05:00:26 PM Casey Schaufler wrote:
> On 8/8/2012 2:54 PM, Eric Dumazet wrote:
>
> By the way, once this proved to be an issue that involved
> more than just SELinux it needed to go onto the LSM list as
> well.

Yes, you're right.

> > On Wed, 2012-08-08 at 16:46 -0400, Paul Moore wrote:
> >> On Wednesday, August 08, 2012 10:32:52 PM Eric Dumazet wrote:
> >>> On Wed, 2012-08-08 at 22:09 +0200, Eric Dumazet wrote:
> >>> +static int smack_sk_alloc_security(struct sock *sk, int ...
> >>> {
> >>> char *csp = smk_of_current();
> >>> struct socket_smack *ssp;
> >>>
> >>> + if (check && sk->sk_security)
> >>> + return 0;
> >>> +
> >>>
> >>> ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
> >>> if (ssp == NULL)
> >>>
> >>> return -ENOMEM;
> >>
> >> In the case of Smack, when the kernel boolean is true I think the right
> >> solution is to use smack_net_ambient.
>
> I confess that my understanding of unicast is limited.
> If the intention is to send an unlabeled packet then
> indeed smack_net_ambient is the way to go.

Well, the intention isn't necessarily to send an unlabeled packet, although
that may be the end result.

In the case of a TCP reset the kernel/ambient label it is hard to argue that
the kernel/ambient label is not the correct solution; in this case there was
never an associated socket so the kernel itself needs to respond.

In the case of a TCP syn-recv and timewait ACK things are a little less clear.
Eric (Dumazet), it looks like we have a socket in tcp_v4_reqsk_send_ack() and
tcp_v4_timewait_ack(), any reason why we can't propagate the socket down to
ip_send_unicast_reply()?

--
paul moore
http://www.paul-moore.com

2012-08-09 14:28:00

by Eric Dumazet

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Thu, 2012-08-09 at 09:30 -0400, Paul Moore wrote:

> In the case of a TCP syn-recv and timewait ACK things are a little less clear.
> Eric (Dumazet), it looks like we have a socket in tcp_v4_reqsk_send_ack() and
> tcp_v4_timewait_ack(), any reason why we can't propagate the socket down to
> ip_send_unicast_reply()?
>

timewait 'sockets' are not full blown sockets.

We need a socket (well, a good part of it) to build the IP frame and
send it.


2012-08-09 14:50:43

by Eric Dumazet

[permalink] [raw]
Subject: [PATCH] ipv4: tcp: security_sk_alloc() needed for unicast_sock

From: Eric Dumazet <[email protected]>

commit be9f4a44e7d41cee (ipv4: tcp: remove per net tcp_sock) added a
selinux regression, reported and bisected by John Stultz

selinux_ip_postroute_compat() expect to find a valid sk->sk_security
pointer, but this field is NULL for unicast_sock

Fix this by adding a new 'kernel' parameter to security_sk_alloc(),
set to true if socket might already have a valid sk->sk_security
pointer. ip_send_unicast_reply() uses a percpu fake socket, so the first
call to security_sk_alloc() will populate sk->sk_security pointer,
subsequent ones will reuse existing context.

Reported-by: John Stultz <[email protected]>
Bisected-by: John Stultz <[email protected]>
Signed-off-by: Eric Dumazet <[email protected]>
Cc: Paul Moore <[email protected]>
Cc: Eric Paris <[email protected]>
Cc: "Serge E. Hallyn" <[email protected]>
---
include/linux/security.h | 6 +++---
net/core/sock.c | 2 +-
net/ipv4/ip_output.c | 4 +++-
security/security.c | 4 ++--
security/selinux/hooks.c | 5 ++++-
security/smack/smack_lsm.c | 10 ++++++++--
6 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/include/linux/security.h b/include/linux/security.h
index 4e5a73c..4d8e454 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1601,7 +1601,7 @@ struct security_operations {
int (*socket_sock_rcv_skb) (struct sock *sk, struct sk_buff *skb);
int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len);
int (*socket_getpeersec_dgram) (struct socket *sock, struct sk_buff *skb, u32 *secid);
- int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority);
+ int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority, bool kernel);
void (*sk_free_security) (struct sock *sk);
void (*sk_clone_security) (const struct sock *sk, struct sock *newsk);
void (*sk_getsecid) (struct sock *sk, u32 *secid);
@@ -2539,7 +2539,7 @@ int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb);
int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
int __user *optlen, unsigned len);
int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid);
-int security_sk_alloc(struct sock *sk, int family, gfp_t priority);
+int security_sk_alloc(struct sock *sk, int family, gfp_t priority, bool kernel);
void security_sk_free(struct sock *sk);
void security_sk_clone(const struct sock *sk, struct sock *newsk);
void security_sk_classify_flow(struct sock *sk, struct flowi *fl);
@@ -2667,7 +2667,7 @@ static inline int security_socket_getpeersec_dgram(struct socket *sock, struct s
return -ENOPROTOOPT;
}

-static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
+static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority, bool kernel)
{
return 0;
}
diff --git a/net/core/sock.c b/net/core/sock.c
index 8f67ced..e00cadf 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1186,7 +1186,7 @@ static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,
if (sk != NULL) {
kmemcheck_annotate_bitfield(sk, flags);

- if (security_sk_alloc(sk, family, priority))
+ if (security_sk_alloc(sk, family, priority, false))
goto out_free;

if (!try_module_get(prot->owner))
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 76dde25..b233d6e 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -1524,6 +1524,8 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
sk->sk_priority = skb->priority;
sk->sk_protocol = ip_hdr(skb)->protocol;
sk->sk_bound_dev_if = arg->bound_dev_if;
+ if (security_sk_alloc(sk, PF_INET, GFP_ATOMIC, true))
+ goto out;
sock_net_set(sk, net);
__skb_queue_head_init(&sk->sk_write_queue);
sk->sk_sndbuf = sysctl_wmem_default;
@@ -1539,7 +1541,7 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
ip_push_pending_frames(sk, &fl4);
}
-
+out:
put_cpu_var(unicast_sock);

ip_rt_put(rt);
diff --git a/security/security.c b/security/security.c
index 860aeb3..23cf297 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1146,9 +1146,9 @@ int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u
}
EXPORT_SYMBOL(security_socket_getpeersec_dgram);

-int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
+int security_sk_alloc(struct sock *sk, int family, gfp_t priority, bool kernel)
{
- return security_ops->sk_alloc_security(sk, family, priority);
+ return security_ops->sk_alloc_security(sk, family, priority, kernel);
}

void security_sk_free(struct sock *sk)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 6c77f63..ccd4374 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -4289,10 +4289,13 @@ out:
return 0;
}

-static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
+static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority, bool kernel)
{
struct sk_security_struct *sksec;

+ if (kernel && sk->sk_security)
+ return 0;
+
sksec = kzalloc(sizeof(*sksec), priority);
if (!sksec)
return -ENOMEM;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 8221514..0b066d0 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1749,20 +1749,26 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
* @sk: the socket
* @family: unused
* @gfp_flags: memory allocation flags
+ * @kernel: true if we should check sk_security being already set
*
* Assign Smack pointers to current
*
* Returns 0 on success, -ENOMEM is there's no memory
*/
-static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
+static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags, bool kernel)
{
- char *csp = smk_of_current();
+ char *csp;
struct socket_smack *ssp;

+ if (kernel && sk->sk_security)
+ return 0;
+
ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
if (ssp == NULL)
return -ENOMEM;

+ csp = kernel ? smack_net_ambient : smk_of_current();
+
ssp->smk_in = csp;
ssp->smk_out = csp;
ssp->smk_packet = NULL;

2012-08-09 15:04:21

by Paul Moore

[permalink] [raw]
Subject: Re: NULL pointer dereference in selinux_ip_postroute_compat

On Thu, Aug 9, 2012 at 10:27 AM, Eric Dumazet <[email protected]> wrote:
> On Thu, 2012-08-09 at 09:30 -0400, Paul Moore wrote:
>
>> In the case of a TCP syn-recv and timewait ACK things are a little less clear.
>> Eric (Dumazet), it looks like we have a socket in tcp_v4_reqsk_send_ack() and
>> tcp_v4_timewait_ack(), any reason why we can't propagate the socket down to
>> ip_send_unicast_reply()?
>>
>
> timewait 'sockets' are not full blown sockets.
>
> We need a socket (well, a good part of it) to build the IP frame and
> send it.

Yes, of course you're right.

Ideally we need a better solution here from a LSM perspective, but I
don't think this should hold up the fix as the labeling was broken
even before the postroute_compat() code broke.

--
paul moore
http://www.paul-moore.com

2012-08-09 15:07:24

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH] ipv4: tcp: security_sk_alloc() needed for unicast_sock

On Thursday, August 09, 2012 04:50:33 PM Eric Dumazet wrote:
> From: Eric Dumazet <[email protected]>
>
> commit be9f4a44e7d41cee (ipv4: tcp: remove per net tcp_sock) added a
> selinux regression, reported and bisected by John Stultz
>
> selinux_ip_postroute_compat() expect to find a valid sk->sk_security
> pointer, but this field is NULL for unicast_sock
>
> Fix this by adding a new 'kernel' parameter to security_sk_alloc(),
> set to true if socket might already have a valid sk->sk_security
> pointer. ip_send_unicast_reply() uses a percpu fake socket, so the first
> call to security_sk_alloc() will populate sk->sk_security pointer,
> subsequent ones will reuse existing context.
>
> Reported-by: John Stultz <[email protected]>
> Bisected-by: John Stultz <[email protected]>
> Signed-off-by: Eric Dumazet <[email protected]>
> Cc: Paul Moore <[email protected]>
> Cc: Eric Paris <[email protected]>
> Cc: "Serge E. Hallyn" <[email protected]>

...

> diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
> index 76dde25..b233d6e 100644
> --- a/net/ipv4/ip_output.c
> +++ b/net/ipv4/ip_output.c
> @@ -1524,6 +1524,8 @@ void ip_send_unicast_reply(struct net *net, struct
> sk_buff *skb, __be32 daddr, sk->sk_priority = skb->priority;
> sk->sk_protocol = ip_hdr(skb)->protocol;
> sk->sk_bound_dev_if = arg->bound_dev_if;
> + if (security_sk_alloc(sk, PF_INET, GFP_ATOMIC, true))
> + goto out;
> sock_net_set(sk, net);
> __skb_queue_head_init(&sk->sk_write_queue);
> sk->sk_sndbuf = sysctl_wmem_default;

Is is possible to do the call to security_sk_alloc() in the ip_init() function
or does the per-cpu nature of the socket make this a pain?

--
paul moore
http://www.paul-moore.com

2012-08-09 15:36:57

by Eric Dumazet

[permalink] [raw]
Subject: Re: [PATCH] ipv4: tcp: security_sk_alloc() needed for unicast_sock

On Thu, 2012-08-09 at 11:07 -0400, Paul Moore wrote:

> Is is possible to do the call to security_sk_alloc() in the ip_init() function
> or does the per-cpu nature of the socket make this a pain?
>

Its a pain, if we want NUMA affinity.

Here, each cpu should get memory from its closest node.


2012-08-09 15:59:26

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH] ipv4: tcp: security_sk_alloc() needed for unicast_sock

On Thu, Aug 9, 2012 at 11:36 AM, Eric Dumazet <[email protected]> wrote:
> On Thu, 2012-08-09 at 11:07 -0400, Paul Moore wrote:
>
>> Is is possible to do the call to security_sk_alloc() in the ip_init() function
>> or does the per-cpu nature of the socket make this a pain?
>>
>
> Its a pain, if we want NUMA affinity.
>
> Here, each cpu should get memory from its closest node.

Okay, makes sense.

Acked-by: Paul Moore <[email protected]>

--
paul moore
http://www.paul-moore.com

2012-08-09 16:05:43

by Eric Paris

[permalink] [raw]
Subject: Re: [PATCH] ipv4: tcp: security_sk_alloc() needed for unicast_sock

On Thu, Aug 9, 2012 at 11:36 AM, Eric Dumazet <[email protected]> wrote:
> On Thu, 2012-08-09 at 11:07 -0400, Paul Moore wrote:
>
>> Is is possible to do the call to security_sk_alloc() in the ip_init() function
>> or does the per-cpu nature of the socket make this a pain?
>>
>
> Its a pain, if we want NUMA affinity.
>
> Here, each cpu should get memory from its closest node.

I really really don't like it. I won't say NAK, but it is the first
and only place in the kernel where I believe we allocate an object and
don't allocate the security blob until some random later point in
time. If it is such a performance issue to have the security blob in
the same numa node, isn't adding a number of branches and putting this
function call on every output at least as bad? Aren't we discouraged
from GFP_ATOMIC? In __init we can use GFP_KERNEL.

This still doesn't fix these sockets entirely. We now have the
security blob allocated, but it was never set to something useful.
Paul, are you looking into this? This is a bandaide, not a fix....

-Eric

2012-08-09 16:09:05

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH] ipv4: tcp: security_sk_alloc() needed for unicast_sock

On Thu, Aug 9, 2012 at 12:05 PM, Eric Paris <[email protected]> wrote:
> Paul, are you looking into this? This is a bandaide, not a fix....

Yep, I mentioned this a few times in the other thread. The problem is
there is not going to be an easy fix for the labeling so I'd rather we
see this patch, or something like it, go in now to resolve the kernel
panic, and fix the labeling later.

--
paul moore
http://www.paul-moore.com

2012-08-09 17:46:21

by Eric Dumazet

[permalink] [raw]
Subject: Re: [PATCH] ipv4: tcp: security_sk_alloc() needed for unicast_sock

On Thu, 2012-08-09 at 12:05 -0400, Eric Paris wrote:
> On Thu, Aug 9, 2012 at 11:36 AM, Eric Dumazet <[email protected]> wrote:
> > On Thu, 2012-08-09 at 11:07 -0400, Paul Moore wrote:
> >
> >> Is is possible to do the call to security_sk_alloc() in the ip_init() function
> >> or does the per-cpu nature of the socket make this a pain?
> >>
> >
> > Its a pain, if we want NUMA affinity.
> >
> > Here, each cpu should get memory from its closest node.
>
> I really really don't like it. I won't say NAK, but it is the first
> and only place in the kernel where I believe we allocate an object and
> don't allocate the security blob until some random later point in
> time.

...

> If it is such a performance issue to have the security blob in
> the same numa node, isn't adding a number of branches and putting this
> function call on every output at least as bad? Aren't we discouraged
> from GFP_ATOMIC? In __init we can use GFP_KERNEL.

What a big deal. Its done _once_ time per cpu, and this is so small blob
of memory you'll have to show us one single failure out of one million
boots.

If the security_sk_alloc() fails, we dont care. We are about sending a
RESET or ACK packet. They can be lost by the network, or even skb
allocation can fail. Nobody ever noticed and complained.

Every time we accept() a new socket (and call security_sk_alloc()), its
done under soft irq, thus GFP_ATOMIC, and you didn't complain yet, while
a socket needs about 2 Kbytes of memory...

>
> This still doesn't fix these sockets entirely. We now have the
> security blob allocated, but it was never set to something useful.
> Paul, are you looking into this? This is a bandaide, not a fix....
>

Please do so, on a followup patch, dont pretend I must fix all this
stuff.

2012-08-09 20:06:23

by Eric Paris

[permalink] [raw]
Subject: Re: [PATCH] ipv4: tcp: security_sk_alloc() needed for unicast_sock

NAK.

I personally think commit be9f4a44e7d41cee should be reverted until it
is fixed. Let me explain what all I believe it broke and how.

Old callchain of the creation of the 'equivalent' socket previous to
the patch in question just for reference:

inet_ctl_sock_create
sock_create_kern
__sock_create
pf->create (inet_create)
sk_alloc
sk_prot_alloc
security_sk_alloc()


This WAS working properly. All of it. The equivalent struct sock was
being created and allocated in inet_create(), which called to
sk_alloc->sk_prot_alloc->security_sk_alloc(). We all agree that
failing to call security_sk_alloc() is the first regression
introduced.

The second regression was the labeling issue. There was a call to
security_socket_post_create (from __sock_create) which was properly
setting the labels on both the socket and sock. This new patch broke
that as well. We don't expose an equivalent
security_sock_post_create() interface in the LSM currently, and until
we do, this can't be fixed. It's why I say it should be reverted.

I have a patch I'm testing right now which takes care of the first
part the way I like (and yes, I'm doing the allocation on the correct
number node). It basically looks like so:

+ for_each_possible_cpu(cpu) {
+ sock = &per_cpu(unicast_sock, cpu);
+ rc = security_sk_alloc(&sock->sk, PF_INET, GFP_KERNEL,
cpu_to_node(cpu));
+ if (rc)
+ return rc;
+ }

I'm going to work right now on exposing the equivalent struct sock LSM
interface so we can call that as well. But it's going to take me a
bit. Attached is the patch just to (hopefully untested) shut up the
panic.

-Eric

On Thu, Aug 9, 2012 at 10:50 AM, Eric Dumazet <[email protected]> wrote:
> From: Eric Dumazet <[email protected]>
>
> commit be9f4a44e7d41cee (ipv4: tcp: remove per net tcp_sock) added a
> selinux regression, reported and bisected by John Stultz
>
> selinux_ip_postroute_compat() expect to find a valid sk->sk_security
> pointer, but this field is NULL for unicast_sock
>
> Fix this by adding a new 'kernel' parameter to security_sk_alloc(),
> set to true if socket might already have a valid sk->sk_security
> pointer. ip_send_unicast_reply() uses a percpu fake socket, so the first
> call to security_sk_alloc() will populate sk->sk_security pointer,
> subsequent ones will reuse existing context.
>
> Reported-by: John Stultz <[email protected]>
> Bisected-by: John Stultz <[email protected]>
> Signed-off-by: Eric Dumazet <[email protected]>
> Cc: Paul Moore <[email protected]>
> Cc: Eric Paris <[email protected]>
> Cc: "Serge E. Hallyn" <[email protected]>
> ---
> include/linux/security.h | 6 +++---
> net/core/sock.c | 2 +-
> net/ipv4/ip_output.c | 4 +++-
> security/security.c | 4 ++--
> security/selinux/hooks.c | 5 ++++-
> security/smack/smack_lsm.c | 10 ++++++++--
> 6 files changed, 21 insertions(+), 10 deletions(-)
>
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 4e5a73c..4d8e454 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -1601,7 +1601,7 @@ struct security_operations {
> int (*socket_sock_rcv_skb) (struct sock *sk, struct sk_buff *skb);
> int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len);
> int (*socket_getpeersec_dgram) (struct socket *sock, struct sk_buff *skb, u32 *secid);
> - int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority);
> + int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority, bool kernel);
> void (*sk_free_security) (struct sock *sk);
> void (*sk_clone_security) (const struct sock *sk, struct sock *newsk);
> void (*sk_getsecid) (struct sock *sk, u32 *secid);
> @@ -2539,7 +2539,7 @@ int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb);
> int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
> int __user *optlen, unsigned len);
> int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid);
> -int security_sk_alloc(struct sock *sk, int family, gfp_t priority);
> +int security_sk_alloc(struct sock *sk, int family, gfp_t priority, bool kernel);
> void security_sk_free(struct sock *sk);
> void security_sk_clone(const struct sock *sk, struct sock *newsk);
> void security_sk_classify_flow(struct sock *sk, struct flowi *fl);
> @@ -2667,7 +2667,7 @@ static inline int security_socket_getpeersec_dgram(struct socket *sock, struct s
> return -ENOPROTOOPT;
> }
>
> -static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
> +static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority, bool kernel)
> {
> return 0;
> }
> diff --git a/net/core/sock.c b/net/core/sock.c
> index 8f67ced..e00cadf 100644
> --- a/net/core/sock.c
> +++ b/net/core/sock.c
> @@ -1186,7 +1186,7 @@ static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,
> if (sk != NULL) {
> kmemcheck_annotate_bitfield(sk, flags);
>
> - if (security_sk_alloc(sk, family, priority))
> + if (security_sk_alloc(sk, family, priority, false))
> goto out_free;
>
> if (!try_module_get(prot->owner))
> diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
> index 76dde25..b233d6e 100644
> --- a/net/ipv4/ip_output.c
> +++ b/net/ipv4/ip_output.c
> @@ -1524,6 +1524,8 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
> sk->sk_priority = skb->priority;
> sk->sk_protocol = ip_hdr(skb)->protocol;
> sk->sk_bound_dev_if = arg->bound_dev_if;
> + if (security_sk_alloc(sk, PF_INET, GFP_ATOMIC, true))
> + goto out;
> sock_net_set(sk, net);
> __skb_queue_head_init(&sk->sk_write_queue);
> sk->sk_sndbuf = sysctl_wmem_default;
> @@ -1539,7 +1541,7 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
> skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
> ip_push_pending_frames(sk, &fl4);
> }
> -
> +out:
> put_cpu_var(unicast_sock);
>
> ip_rt_put(rt);
> diff --git a/security/security.c b/security/security.c
> index 860aeb3..23cf297 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -1146,9 +1146,9 @@ int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u
> }
> EXPORT_SYMBOL(security_socket_getpeersec_dgram);
>
> -int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
> +int security_sk_alloc(struct sock *sk, int family, gfp_t priority, bool kernel)
> {
> - return security_ops->sk_alloc_security(sk, family, priority);
> + return security_ops->sk_alloc_security(sk, family, priority, kernel);
> }
>
> void security_sk_free(struct sock *sk)
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 6c77f63..ccd4374 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -4289,10 +4289,13 @@ out:
> return 0;
> }
>
> -static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
> +static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority, bool kernel)
> {
> struct sk_security_struct *sksec;
>
> + if (kernel && sk->sk_security)
> + return 0;
> +
> sksec = kzalloc(sizeof(*sksec), priority);
> if (!sksec)
> return -ENOMEM;
> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index 8221514..0b066d0 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -1749,20 +1749,26 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
> * @sk: the socket
> * @family: unused
> * @gfp_flags: memory allocation flags
> + * @kernel: true if we should check sk_security being already set
> *
> * Assign Smack pointers to current
> *
> * Returns 0 on success, -ENOMEM is there's no memory
> */
> -static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
> +static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags, bool kernel)
> {
> - char *csp = smk_of_current();
> + char *csp;
> struct socket_smack *ssp;
>
> + if (kernel && sk->sk_security)
> + return 0;
> +
> ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
> if (ssp == NULL)
> return -ENOMEM;
>
> + csp = kernel ? smack_net_ambient : smk_of_current();
> +
> ssp->smk_in = csp;
> ssp->smk_out = csp;
> ssp->smk_packet = NULL;
>
>


Attachments:
tmp.patch (12.37 kB)

2012-08-09 20:20:40

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH] ipv4: tcp: security_sk_alloc() needed for unicast_sock

On Thu, Aug 9, 2012 at 4:06 PM, Eric Paris <[email protected]> wrote:
> I'm going to work right now on exposing the equivalent struct sock LSM
> interface so we can call that as well. But it's going to take me a
> bit.

Before you go too far down this path, can you elaborate on what
exactly you mean by the above?

I'm asking because I'm not convinced the labeling, either the old way
or the new way, was 100% correct and I think we're going to need to
change things regardless. I'm just not sure what the right solution
is just yet.

--
paul moore
http://www.paul-moore.com

2012-08-09 21:29:15

by Eric Dumazet

[permalink] [raw]
Subject: Re: [PATCH] ipv4: tcp: security_sk_alloc() needed for unicast_sock

On Thu, 2012-08-09 at 16:06 -0400, Eric Paris wrote:
> NAK.
>
> I personally think commit be9f4a44e7d41cee should be reverted until it
> is fixed. Let me explain what all I believe it broke and how.
>

Suggesting to revert this commit while we have known working fixes is a
bit of strange reaction.

I understand you are upset, but I believe we tried to fix it.

> Old callchain of the creation of the 'equivalent' socket previous to
> the patch in question just for reference:
>
> inet_ctl_sock_create
> sock_create_kern
> __sock_create
> pf->create (inet_create)
> sk_alloc
> sk_prot_alloc
> security_sk_alloc()
>
>
> This WAS working properly. All of it.

Nobody denies it. But acknowledge my patch uncovered a fundamental
issue.

What kind of 'security module' can decide to let RST packets being sent
or not, on a global scale ? (one socket for the whole machine)

smack_sk_alloc_security() uses smk_of_current() : What can be the
meaning of smk_of_current() in the context of 'kernel' sockets...

Your patch tries to maintain this status quo.

In fact I suggest the following one liner patch, unless you can really
demonstrate what can be the meaning of providing a fake socket for these
packets.

This mess only happened because ip_append_data()/ip_push_pending_frames()
are so complex and use an underlying socket.

But this socket should not be ever used outside of its scope.

diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 76dde25..ec410e0 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -1536,6 +1536,7 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
arg->csumoffset) = csum_fold(csum_add(nskb->csum,
arg->csum));
nskb->ip_summed = CHECKSUM_NONE;
+ skb_orphan(nskb);
skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
ip_push_pending_frames(sk, &fl4);
}

2012-08-09 21:53:07

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] ipv4: tcp: security_sk_alloc() needed for unicast_sock

On 8/9/2012 2:29 PM, Eric Dumazet wrote:
> On Thu, 2012-08-09 at 16:06 -0400, Eric Paris wrote:
>> NAK.
>>
>> I personally think commit be9f4a44e7d41cee should be reverted until it
>> is fixed. Let me explain what all I believe it broke and how.
>>
> Suggesting to revert this commit while we have known working fixes is a
> bit of strange reaction.

A couple of potential short term workarounds have been identified,
but no one is happy with them for the long term. That does not
qualify as a "working fix" in engineering terms.

> I understand you are upset, but I believe we tried to fix it.
>
>> Old callchain of the creation of the 'equivalent' socket previous to
>> the patch in question just for reference:
>>
>> inet_ctl_sock_create
>> sock_create_kern
>> __sock_create
>> pf->create (inet_create)
>> sk_alloc
>> sk_prot_alloc
>> security_sk_alloc()
>>
>>
>> This WAS working properly. All of it.
> Nobody denies it. But acknowledge my patch uncovered a fundamental
> issue.
>
> What kind of 'security module' can decide to let RST packets being sent
> or not, on a global scale ? (one socket for the whole machine)

The short answer is "any security module that wants to".

And before we go any further, I'm a little surprised that
SELinux doesn't do this already.

>
> smack_sk_alloc_security() uses smk_of_current() : What can be the
> meaning of smk_of_current() in the context of 'kernel' sockets...

Yes, and all of it's callers - to date - have had an appropriate
value of current. It is using the API in the way it is supposed to.
It is assuming a properly formed socket. You want to give it a
cobbled together partial socket structure without task context.
Your predecessor did not have this problem.

>
> Your patch tries to maintain this status quo.
>
> In fact I suggest the following one liner patch, unless you can really
> demonstrate what can be the meaning of providing a fake socket for these
> packets.
>
> This mess only happened because ip_append_data()/ip_push_pending_frames()
> are so complex and use an underlying socket.
>
> But this socket should not be ever used outside of its scope.
>
> diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
> index 76dde25..ec410e0 100644
> --- a/net/ipv4/ip_output.c
> +++ b/net/ipv4/ip_output.c
> @@ -1536,6 +1536,7 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
> arg->csumoffset) = csum_fold(csum_add(nskb->csum,
> arg->csum));
> nskb->ip_summed = CHECKSUM_NONE;
> + skb_orphan(nskb);
> skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
> ip_push_pending_frames(sk, &fl4);
> }
>
>
>

2012-08-09 22:05:28

by Eric Dumazet

[permalink] [raw]
Subject: Re: [PATCH] ipv4: tcp: security_sk_alloc() needed for unicast_sock

On Thu, 2012-08-09 at 14:53 -0700, Casey Schaufler wrote:
> On 8/9/2012 2:29 PM, Eric Dumazet wrote:

> > smack_sk_alloc_security() uses smk_of_current() : What can be the
> > meaning of smk_of_current() in the context of 'kernel' sockets...
>
> Yes, and all of it's callers - to date - have had an appropriate
> value of current. It is using the API in the way it is supposed to.
> It is assuming a properly formed socket. You want to give it a
> cobbled together partial socket structure without task context.
> Your predecessor did not have this problem.

My predecessor ? You mean before the patch ?

tcp socket was preallocated by at kernel boot time.

What is the 'user' owning this socket ?

You guys focus on an implementation detail of TCP stack.
You should never use this fake socket.

I repeat : There are no true socket for these control packets.

If you want them, then you'll have to add fields in timewait socket.

I can decide to rewrite the whole thing just building a TCP packet on
its own, and send it without any fake socket.

Some guy 15 years ago tried to reuse some high level functions, able to
build super packets and use sophisticated tricks, while we only want so
send a 40 or 60 bytes packet.


2012-08-09 22:32:24

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] ipv4: tcp: security_sk_alloc() needed for unicast_sock

On 8/9/2012 3:05 PM, Eric Dumazet wrote:
> On Thu, 2012-08-09 at 14:53 -0700, Casey Schaufler wrote:
>> On 8/9/2012 2:29 PM, Eric Dumazet wrote:
>>> smack_sk_alloc_security() uses smk_of_current() : What can be the
>>> meaning of smk_of_current() in the context of 'kernel' sockets...
>> Yes, and all of it's callers - to date - have had an appropriate
>> value of current. It is using the API in the way it is supposed to.
>> It is assuming a properly formed socket. You want to give it a
>> cobbled together partial socket structure without task context.
>> Your predecessor did not have this problem.
> My predecessor ? You mean before the patch ?
>
> tcp socket was preallocated by at kernel boot time.
>
> What is the 'user' owning this socket ?
>
> You guys focus on an implementation detail of TCP stack.
> You should never use this fake socket.
>
> I repeat : There are no true socket for these control packets.
>
> If you want them, then you'll have to add fields in timewait socket.
>
> I can decide to rewrite the whole thing just building a TCP packet on
> its own, and send it without any fake socket.
>
> Some guy 15 years ago tried to reuse some high level functions, able to
> build super packets and use sophisticated tricks, while we only want so
> send a 40 or 60 bytes packet.

OK, fine. You have an optimization. I'm good with that. Just don't
expect that the entire software stack you are taking advantage of
is going to change to accommodate your special case.

2012-08-09 23:38:54

by David Miller

[permalink] [raw]
Subject: Re: [PATCH] ipv4: tcp: security_sk_alloc() needed for unicast_sock

From: Eric Dumazet <[email protected]>
Date: Thu, 09 Aug 2012 23:29:03 +0200

> diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
> index 76dde25..ec410e0 100644
> --- a/net/ipv4/ip_output.c
> +++ b/net/ipv4/ip_output.c
> @@ -1536,6 +1536,7 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
> arg->csumoffset) = csum_fold(csum_add(nskb->csum,
> arg->csum));
> nskb->ip_summed = CHECKSUM_NONE;
> + skb_orphan(nskb);
> skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
> ip_push_pending_frames(sk, &fl4);
> }
>

This is definitely the best fix, please submit this formally.

2012-08-09 23:56:15

by Eric Dumazet

[permalink] [raw]
Subject: [PATCH] ipv4: tcp: unicast_sock should not land outside of TCP stack

From: Eric Dumazet <[email protected]>

commit be9f4a44e7d41cee (ipv4: tcp: remove per net tcp_sock) added a
selinux regression, reported and bisected by John Stultz

selinux_ip_postroute_compat() expect to find a valid sk->sk_security
pointer, but this field is NULL for unicast_sock

It turns out that unicast_sock are really temporary stuff to be able
to reuse part of IP stack (ip_append_data()/ip_push_pending_frames())

Fact is that frames sent by ip_send_unicast_reply() should be orphaned
to not fool LSM.

Note IPv6 never had this problem, as tcp_v6_send_response() doesnt use a
fake socket at all. I'll probably implement tcp_v4_send_response() to
remove these unicast_sock in linux-3.7

Reported-by: John Stultz <[email protected]>
Bisected-by: John Stultz <[email protected]>
Signed-off-by: Eric Dumazet <[email protected]>
Cc: Paul Moore <[email protected]>
Cc: Eric Paris <[email protected]>
Cc: "Serge E. Hallyn" <[email protected]>
---
net/ipv4/ip_output.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 76dde25..ec410e0 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -1536,6 +1536,7 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
arg->csumoffset) = csum_fold(csum_add(nskb->csum,
arg->csum));
nskb->ip_summed = CHECKSUM_NONE;
+ skb_orphan(nskb);
skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
ip_push_pending_frames(sk, &fl4);
}

2012-08-10 04:05:51

by David Miller

[permalink] [raw]
Subject: Re: [PATCH] ipv4: tcp: unicast_sock should not land outside of TCP stack

From: Eric Dumazet <[email protected]>
Date: Fri, 10 Aug 2012 01:56:06 +0200

> From: Eric Dumazet <[email protected]>
>
> commit be9f4a44e7d41cee (ipv4: tcp: remove per net tcp_sock) added a
> selinux regression, reported and bisected by John Stultz
>
> selinux_ip_postroute_compat() expect to find a valid sk->sk_security
> pointer, but this field is NULL for unicast_sock
>
> It turns out that unicast_sock are really temporary stuff to be able
> to reuse part of IP stack (ip_append_data()/ip_push_pending_frames())
>
> Fact is that frames sent by ip_send_unicast_reply() should be orphaned
> to not fool LSM.
>
> Note IPv6 never had this problem, as tcp_v6_send_response() doesnt use a
> fake socket at all. I'll probably implement tcp_v4_send_response() to
> remove these unicast_sock in linux-3.7
>
> Reported-by: John Stultz <[email protected]>
> Bisected-by: John Stultz <[email protected]>
> Signed-off-by: Eric Dumazet <[email protected]>

Applied.