diff mbox series

[net] can: raw: fix raw_rcv panic for sock UAF

Message ID 20210721010937.670275-1-william.xuanziyang@huawei.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series [net] can: raw: fix raw_rcv panic for sock UAF | expand

Checks

Context Check Description
netdev/cover_letter success Link
netdev/fixes_present success Link
netdev/patch_count success Link
netdev/tree_selection success Clearly marked for net
netdev/subject_prefix success Link
netdev/cc_maintainers fail 1 blamed authors not CCed: urs.thuermann@volkswagen.de; 1 maintainers not CCed: urs.thuermann@volkswagen.de
netdev/source_inline success Was 0 now: 0
netdev/verify_signedoff success Link
netdev/module_param success Was 0 now: 0
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/verify_fixes success Link
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 28 lines checked
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/header_inline success Link

Commit Message

Ziyang Xuan (William) July 21, 2021, 1:09 a.m. UTC
We get a bug during ltp can_filter test as following.

===========================================
[60919.264984] BUG: unable to handle kernel NULL pointer dereference at 0000000000000010
[60919.265223] PGD 8000003dda726067 P4D 8000003dda726067 PUD 3dda727067 PMD 0
[60919.265443] Oops: 0000 [#1] SMP PTI
[60919.265550] CPU: 30 PID: 3638365 Comm: can_filter Kdump: loaded Tainted: G        W         4.19.90+ #1
[60919.266068] RIP: 0010:selinux_socket_sock_rcv_skb+0x3e/0x200
[60919.293289] RSP: 0018:ffff8d53bfc03cf8 EFLAGS: 00010246
[60919.307140] RAX: 0000000000000000 RBX: 000000000000001d RCX: 0000000000000007
[60919.320756] RDX: 0000000000000001 RSI: ffff8d5104a8ed00 RDI: ffff8d53bfc03d30
[60919.334319] RBP: ffff8d9338056800 R08: ffff8d53bfc29d80 R09: 0000000000000001
[60919.347969] R10: ffff8d53bfc03ec0 R11: ffffb8526ef47c98 R12: ffff8d53bfc03d30
[60919.350320] perf: interrupt took too long (3063 > 2500), lowering kernel.perf_event_max_sample_rate to 65000
[60919.361148] R13: 0000000000000001 R14: ffff8d53bcf90000 R15: 0000000000000000
[60919.361151] FS:  00007fb78b6b3600(0000) GS:ffff8d53bfc00000(0000) knlGS:0000000000000000
[60919.400812] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[60919.413730] CR2: 0000000000000010 CR3: 0000003e3f784006 CR4: 00000000007606e0
[60919.426479] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[60919.439339] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[60919.451608] PKRU: 55555554
[60919.463622] Call Trace:
[60919.475617]  <IRQ>
[60919.487122]  ? update_load_avg+0x89/0x5d0
[60919.498478]  ? update_load_avg+0x89/0x5d0
[60919.509822]  ? account_entity_enqueue+0xc5/0xf0
[60919.520709]  security_sock_rcv_skb+0x2a/0x40
[60919.531413]  sk_filter_trim_cap+0x47/0x1b0
[60919.542178]  ? kmem_cache_alloc+0x38/0x1b0
[60919.552444]  sock_queue_rcv_skb+0x17/0x30
[60919.562477]  raw_rcv+0x110/0x190 [can_raw]
[60919.572539]  can_rcv_filter+0xbc/0x1b0 [can]
[60919.582173]  can_receive+0x6b/0xb0 [can]
[60919.591595]  can_rcv+0x31/0x70 [can]
[60919.600783]  __netif_receive_skb_one_core+0x5a/0x80
[60919.609864]  process_backlog+0x9b/0x150
[60919.618691]  net_rx_action+0x156/0x400
[60919.627310]  ? sched_clock_cpu+0xc/0xa0
[60919.635714]  __do_softirq+0xe8/0x2e9
[60919.644161]  do_softirq_own_stack+0x2a/0x40
[60919.652154]  </IRQ>
[60919.659899]  do_softirq.part.17+0x4f/0x60
[60919.667475]  __local_bh_enable_ip+0x60/0x70
[60919.675089]  __dev_queue_xmit+0x539/0x920
[60919.682267]  ? finish_wait+0x80/0x80
[60919.689218]  ? finish_wait+0x80/0x80
[60919.695886]  ? sock_alloc_send_pskb+0x211/0x230
[60919.702395]  ? can_send+0xe5/0x1f0 [can]
[60919.708882]  can_send+0xe5/0x1f0 [can]
[60919.715037]  raw_sendmsg+0x16d/0x268 [can_raw]

It's because raw_setsockopt() concurrently with
unregister_netdevice_many(). Concurrent scenario as following.

	cpu0						cpu1
raw_bind
raw_setsockopt					unregister_netdevice_many
						unlist_netdevice
dev_get_by_index				raw_notifier
raw_enable_filters				......
can_rx_register
can_rcv_list_find(..., net->can.rx_alldev_list)

......

sock_close
raw_release(sock_a)

......

can_receive
can_rcv_filter(net->can.rx_alldev_list, ...)
raw_rcv(skb, sock_a)
BUG

After unlist_netdevice(), dev_get_by_index() return NULL in
raw_setsockopt(). Function raw_enable_filters() will add sock
and can_filter to net->can.rx_alldev_list. Then the sock is closed.
Followed by, we sock_sendmsg() to a new vcan device use the same
can_filter. Protocol stack match the old receiver whose sock has
been released on net->can.rx_alldev_list in can_rcv_filter().
Function raw_rcv() uses the freed sock. UAF BUG is triggered.

We can find that the key issue is that net_device has not been
protected in raw_setsockopt(). Use rtnl_lock to protect net_device
in raw_setsockopt().

Fixes: c18ce101f2e4 ("[CAN]: Add raw protocol")
Signed-off-by: Ziyang Xuan <william.xuanziyang@huawei.com>
---
 net/can/raw.c | 4 ++++
 1 file changed, 4 insertions(+)

Comments

Greg KH July 21, 2021, 4:53 a.m. UTC | #1
On Wed, Jul 21, 2021 at 09:09:37AM +0800, Ziyang Xuan wrote:
> We get a bug during ltp can_filter test as following.
> 
> ===========================================
> [60919.264984] BUG: unable to handle kernel NULL pointer dereference at 0000000000000010
> [60919.265223] PGD 8000003dda726067 P4D 8000003dda726067 PUD 3dda727067 PMD 0
> [60919.265443] Oops: 0000 [#1] SMP PTI
> [60919.265550] CPU: 30 PID: 3638365 Comm: can_filter Kdump: loaded Tainted: G        W         4.19.90+ #1
> [60919.266068] RIP: 0010:selinux_socket_sock_rcv_skb+0x3e/0x200
> [60919.293289] RSP: 0018:ffff8d53bfc03cf8 EFLAGS: 00010246
> [60919.307140] RAX: 0000000000000000 RBX: 000000000000001d RCX: 0000000000000007
> [60919.320756] RDX: 0000000000000001 RSI: ffff8d5104a8ed00 RDI: ffff8d53bfc03d30
> [60919.334319] RBP: ffff8d9338056800 R08: ffff8d53bfc29d80 R09: 0000000000000001
> [60919.347969] R10: ffff8d53bfc03ec0 R11: ffffb8526ef47c98 R12: ffff8d53bfc03d30
> [60919.350320] perf: interrupt took too long (3063 > 2500), lowering kernel.perf_event_max_sample_rate to 65000
> [60919.361148] R13: 0000000000000001 R14: ffff8d53bcf90000 R15: 0000000000000000
> [60919.361151] FS:  00007fb78b6b3600(0000) GS:ffff8d53bfc00000(0000) knlGS:0000000000000000
> [60919.400812] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> [60919.413730] CR2: 0000000000000010 CR3: 0000003e3f784006 CR4: 00000000007606e0
> [60919.426479] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> [60919.439339] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
> [60919.451608] PKRU: 55555554
> [60919.463622] Call Trace:
> [60919.475617]  <IRQ>
> [60919.487122]  ? update_load_avg+0x89/0x5d0
> [60919.498478]  ? update_load_avg+0x89/0x5d0
> [60919.509822]  ? account_entity_enqueue+0xc5/0xf0
> [60919.520709]  security_sock_rcv_skb+0x2a/0x40
> [60919.531413]  sk_filter_trim_cap+0x47/0x1b0
> [60919.542178]  ? kmem_cache_alloc+0x38/0x1b0
> [60919.552444]  sock_queue_rcv_skb+0x17/0x30
> [60919.562477]  raw_rcv+0x110/0x190 [can_raw]
> [60919.572539]  can_rcv_filter+0xbc/0x1b0 [can]
> [60919.582173]  can_receive+0x6b/0xb0 [can]
> [60919.591595]  can_rcv+0x31/0x70 [can]
> [60919.600783]  __netif_receive_skb_one_core+0x5a/0x80
> [60919.609864]  process_backlog+0x9b/0x150
> [60919.618691]  net_rx_action+0x156/0x400
> [60919.627310]  ? sched_clock_cpu+0xc/0xa0
> [60919.635714]  __do_softirq+0xe8/0x2e9
> [60919.644161]  do_softirq_own_stack+0x2a/0x40
> [60919.652154]  </IRQ>
> [60919.659899]  do_softirq.part.17+0x4f/0x60
> [60919.667475]  __local_bh_enable_ip+0x60/0x70
> [60919.675089]  __dev_queue_xmit+0x539/0x920
> [60919.682267]  ? finish_wait+0x80/0x80
> [60919.689218]  ? finish_wait+0x80/0x80
> [60919.695886]  ? sock_alloc_send_pskb+0x211/0x230
> [60919.702395]  ? can_send+0xe5/0x1f0 [can]
> [60919.708882]  can_send+0xe5/0x1f0 [can]
> [60919.715037]  raw_sendmsg+0x16d/0x268 [can_raw]
> 
> It's because raw_setsockopt() concurrently with
> unregister_netdevice_many(). Concurrent scenario as following.
> 
> 	cpu0						cpu1
> raw_bind
> raw_setsockopt					unregister_netdevice_many
> 						unlist_netdevice
> dev_get_by_index				raw_notifier
> raw_enable_filters				......
> can_rx_register
> can_rcv_list_find(..., net->can.rx_alldev_list)
> 
> ......
> 
> sock_close
> raw_release(sock_a)
> 
> ......
> 
> can_receive
> can_rcv_filter(net->can.rx_alldev_list, ...)
> raw_rcv(skb, sock_a)
> BUG
> 
> After unlist_netdevice(), dev_get_by_index() return NULL in
> raw_setsockopt(). Function raw_enable_filters() will add sock
> and can_filter to net->can.rx_alldev_list. Then the sock is closed.
> Followed by, we sock_sendmsg() to a new vcan device use the same
> can_filter. Protocol stack match the old receiver whose sock has
> been released on net->can.rx_alldev_list in can_rcv_filter().
> Function raw_rcv() uses the freed sock. UAF BUG is triggered.
> 
> We can find that the key issue is that net_device has not been
> protected in raw_setsockopt(). Use rtnl_lock to protect net_device
> in raw_setsockopt().
> 
> Fixes: c18ce101f2e4 ("[CAN]: Add raw protocol")
> Signed-off-by: Ziyang Xuan <william.xuanziyang@huawei.com>
> ---
>  net/can/raw.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/net/can/raw.c b/net/can/raw.c
> index ed4fcb7ab0c3..a63e9915c66a 100644
> --- a/net/can/raw.c
> +++ b/net/can/raw.c
> @@ -546,6 +546,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>  				return -EFAULT;
>  		}
>  
> +		rtnl_lock();
>  		lock_sock(sk);
>  
>  		if (ro->bound && ro->ifindex)
> @@ -588,6 +589,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>  			dev_put(dev);
>  
>  		release_sock(sk);
> +		rtnl_unlock();
>  
>  		break;
>  
> @@ -600,6 +602,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>  
>  		err_mask &= CAN_ERR_MASK;
>  
> +		rtnl_lock();
>  		lock_sock(sk);
>  
>  		if (ro->bound && ro->ifindex)
> @@ -627,6 +630,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>  			dev_put(dev);
>  
>  		release_sock(sk);
> +		rtnl_unlock();
>  
>  		break;
>  
> -- 
> 2.25.1
> 


<formletter>

This is not the correct way to submit patches for inclusion in the
stable kernel tree.  Please read:
    https://www.kernel.org/doc/html/latest/process/stable-kernel-rules.html
for how to do this properly.

</formletter>
Oliver Hartkopp July 21, 2021, 6:35 a.m. UTC | #2
On 21.07.21 06:53, Greg KH wrote:
> On Wed, Jul 21, 2021 at 09:09:37AM +0800, Ziyang Xuan wrote:
>> We get a bug during ltp can_filter test as following.
>>
>> ===========================================
>> [60919.264984] BUG: unable to handle kernel NULL pointer dereference at 0000000000000010
>> [60919.265223] PGD 8000003dda726067 P4D 8000003dda726067 PUD 3dda727067 PMD 0
>> [60919.265443] Oops: 0000 [#1] SMP PTI
>> [60919.265550] CPU: 30 PID: 3638365 Comm: can_filter Kdump: loaded Tainted: G        W         4.19.90+ #1

This kernel version 4.19.90 is definitely outdated.

Can you please check your issue with the latest uptream kernel as this 
problem should have been fixed with this patch:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8d0caedb759683041d9db82069937525999ada53
("can: bcm/raw/isotp: use per module netdevice notifier")

Thanks!

>> [60919.266068] RIP: 0010:selinux_socket_sock_rcv_skb+0x3e/0x200
>> [60919.293289] RSP: 0018:ffff8d53bfc03cf8 EFLAGS: 00010246
>> [60919.307140] RAX: 0000000000000000 RBX: 000000000000001d RCX: 0000000000000007
>> [60919.320756] RDX: 0000000000000001 RSI: ffff8d5104a8ed00 RDI: ffff8d53bfc03d30
>> [60919.334319] RBP: ffff8d9338056800 R08: ffff8d53bfc29d80 R09: 0000000000000001
>> [60919.347969] R10: ffff8d53bfc03ec0 R11: ffffb8526ef47c98 R12: ffff8d53bfc03d30
>> [60919.350320] perf: interrupt took too long (3063 > 2500), lowering kernel.perf_event_max_sample_rate to 65000
>> [60919.361148] R13: 0000000000000001 R14: ffff8d53bcf90000 R15: 0000000000000000
>> [60919.361151] FS:  00007fb78b6b3600(0000) GS:ffff8d53bfc00000(0000) knlGS:0000000000000000
>> [60919.400812] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
>> [60919.413730] CR2: 0000000000000010 CR3: 0000003e3f784006 CR4: 00000000007606e0
>> [60919.426479] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
>> [60919.439339] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
>> [60919.451608] PKRU: 55555554
>> [60919.463622] Call Trace:
>> [60919.475617]  <IRQ>
>> [60919.487122]  ? update_load_avg+0x89/0x5d0
>> [60919.498478]  ? update_load_avg+0x89/0x5d0
>> [60919.509822]  ? account_entity_enqueue+0xc5/0xf0
>> [60919.520709]  security_sock_rcv_skb+0x2a/0x40
>> [60919.531413]  sk_filter_trim_cap+0x47/0x1b0
>> [60919.542178]  ? kmem_cache_alloc+0x38/0x1b0
>> [60919.552444]  sock_queue_rcv_skb+0x17/0x30
>> [60919.562477]  raw_rcv+0x110/0x190 [can_raw]
>> [60919.572539]  can_rcv_filter+0xbc/0x1b0 [can]
>> [60919.582173]  can_receive+0x6b/0xb0 [can]
>> [60919.591595]  can_rcv+0x31/0x70 [can]
>> [60919.600783]  __netif_receive_skb_one_core+0x5a/0x80
>> [60919.609864]  process_backlog+0x9b/0x150
>> [60919.618691]  net_rx_action+0x156/0x400
>> [60919.627310]  ? sched_clock_cpu+0xc/0xa0
>> [60919.635714]  __do_softirq+0xe8/0x2e9
>> [60919.644161]  do_softirq_own_stack+0x2a/0x40
>> [60919.652154]  </IRQ>
>> [60919.659899]  do_softirq.part.17+0x4f/0x60
>> [60919.667475]  __local_bh_enable_ip+0x60/0x70
>> [60919.675089]  __dev_queue_xmit+0x539/0x920
>> [60919.682267]  ? finish_wait+0x80/0x80
>> [60919.689218]  ? finish_wait+0x80/0x80
>> [60919.695886]  ? sock_alloc_send_pskb+0x211/0x230
>> [60919.702395]  ? can_send+0xe5/0x1f0 [can]
>> [60919.708882]  can_send+0xe5/0x1f0 [can]
>> [60919.715037]  raw_sendmsg+0x16d/0x268 [can_raw]
>>
>> It's because raw_setsockopt() concurrently with
>> unregister_netdevice_many(). Concurrent scenario as following.
>>
>> 	cpu0						cpu1
>> raw_bind
>> raw_setsockopt					unregister_netdevice_many
>> 						unlist_netdevice
>> dev_get_by_index				raw_notifier
>> raw_enable_filters				......
>> can_rx_register
>> can_rcv_list_find(..., net->can.rx_alldev_list)
>>
>> ......
>>
>> sock_close
>> raw_release(sock_a)
>>
>> ......
>>
>> can_receive
>> can_rcv_filter(net->can.rx_alldev_list, ...)
>> raw_rcv(skb, sock_a)
>> BUG
>>
>> After unlist_netdevice(), dev_get_by_index() return NULL in
>> raw_setsockopt(). Function raw_enable_filters() will add sock
>> and can_filter to net->can.rx_alldev_list. Then the sock is closed.
>> Followed by, we sock_sendmsg() to a new vcan device use the same
>> can_filter. Protocol stack match the old receiver whose sock has
>> been released on net->can.rx_alldev_list in can_rcv_filter().
>> Function raw_rcv() uses the freed sock. UAF BUG is triggered.
>>
>> We can find that the key issue is that net_device has not been
>> protected in raw_setsockopt(). Use rtnl_lock to protect net_device
>> in raw_setsockopt().
>>
>> Fixes: c18ce101f2e4 ("[CAN]: Add raw protocol")
>> Signed-off-by: Ziyang Xuan <william.xuanziyang@huawei.com>
>> ---
>>   net/can/raw.c | 4 ++++
>>   1 file changed, 4 insertions(+)
>>
>> diff --git a/net/can/raw.c b/net/can/raw.c
>> index ed4fcb7ab0c3..a63e9915c66a 100644
>> --- a/net/can/raw.c
>> +++ b/net/can/raw.c
>> @@ -546,6 +546,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>>   				return -EFAULT;
>>   		}
>>   
>> +		rtnl_lock();
>>   		lock_sock(sk);
>>   
>>   		if (ro->bound && ro->ifindex)
>> @@ -588,6 +589,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>>   			dev_put(dev);
>>   
>>   		release_sock(sk);
>> +		rtnl_unlock();
>>   
>>   		break;
>>   
>> @@ -600,6 +602,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>>   
>>   		err_mask &= CAN_ERR_MASK;
>>   
>> +		rtnl_lock();
>>   		lock_sock(sk);
>>   
>>   		if (ro->bound && ro->ifindex)
>> @@ -627,6 +630,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>>   			dev_put(dev);
>>   
>>   		release_sock(sk);
>> +		rtnl_unlock();
>>   
>>   		break;
>>   
>> -- 
>> 2.25.1
>>
> 
> 
> <formletter>
> 
> This is not the correct way to submit patches for inclusion in the
> stable kernel tree.  Please read:
>      https://www.kernel.org/doc/html/latest/process/stable-kernel-rules.html
> for how to do this properly.
> 
> </formletter>
>
Oliver Hartkopp July 21, 2021, 9:24 a.m. UTC | #3
Answering myself ...

On 21.07.21 08:35, Oliver Hartkopp wrote:
> 
> 
> On 21.07.21 06:53, Greg KH wrote:
>> On Wed, Jul 21, 2021 at 09:09:37AM +0800, Ziyang Xuan wrote:
>>> We get a bug during ltp can_filter test as following.
>>>
>>> ===========================================
>>> [60919.264984] BUG: unable to handle kernel NULL pointer dereference 
>>> at 0000000000000010
>>> [60919.265223] PGD 8000003dda726067 P4D 8000003dda726067 PUD 
>>> 3dda727067 PMD 0
>>> [60919.265443] Oops: 0000 [#1] SMP PTI
>>> [60919.265550] CPU: 30 PID: 3638365 Comm: can_filter Kdump: loaded 
>>> Tainted: G        W         4.19.90+ #1
> 
> This kernel version 4.19.90 is definitely outdated.
> 
> Can you please check your issue with the latest uptream kernel as this 
> problem should have been fixed with this patch:
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8d0caedb759683041d9db82069937525999ada53 
> 
> ("can: bcm/raw/isotp: use per module netdevice notifier")
> 
> Thanks!

I think my hint had a wrong assumption. The suggestion to add some 
locking seems correct.

>>> [60919.266068] RIP: 0010:selinux_socket_sock_rcv_skb+0x3e/0x200
>>> [60919.293289] RSP: 0018:ffff8d53bfc03cf8 EFLAGS: 00010246
>>> [60919.307140] RAX: 0000000000000000 RBX: 000000000000001d RCX: 
>>> 0000000000000007
>>> [60919.320756] RDX: 0000000000000001 RSI: ffff8d5104a8ed00 RDI: 
>>> ffff8d53bfc03d30
>>> [60919.334319] RBP: ffff8d9338056800 R08: ffff8d53bfc29d80 R09: 
>>> 0000000000000001
>>> [60919.347969] R10: ffff8d53bfc03ec0 R11: ffffb8526ef47c98 R12: 
>>> ffff8d53bfc03d30
>>> [60919.350320] perf: interrupt took too long (3063 > 2500), lowering 
>>> kernel.perf_event_max_sample_rate to 65000
>>> [60919.361148] R13: 0000000000000001 R14: ffff8d53bcf90000 R15: 
>>> 0000000000000000
>>> [60919.361151] FS:  00007fb78b6b3600(0000) GS:ffff8d53bfc00000(0000) 
>>> knlGS:0000000000000000
>>> [60919.400812] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
>>> [60919.413730] CR2: 0000000000000010 CR3: 0000003e3f784006 CR4: 
>>> 00000000007606e0
>>> [60919.426479] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 
>>> 0000000000000000
>>> [60919.439339] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 
>>> 0000000000000400
>>> [60919.451608] PKRU: 55555554
>>> [60919.463622] Call Trace:
>>> [60919.475617]  <IRQ>
>>> [60919.487122]  ? update_load_avg+0x89/0x5d0
>>> [60919.498478]  ? update_load_avg+0x89/0x5d0
>>> [60919.509822]  ? account_entity_enqueue+0xc5/0xf0
>>> [60919.520709]  security_sock_rcv_skb+0x2a/0x40
>>> [60919.531413]  sk_filter_trim_cap+0x47/0x1b0
>>> [60919.542178]  ? kmem_cache_alloc+0x38/0x1b0
>>> [60919.552444]  sock_queue_rcv_skb+0x17/0x30
>>> [60919.562477]  raw_rcv+0x110/0x190 [can_raw]
>>> [60919.572539]  can_rcv_filter+0xbc/0x1b0 [can]
>>> [60919.582173]  can_receive+0x6b/0xb0 [can]
>>> [60919.591595]  can_rcv+0x31/0x70 [can]
>>> [60919.600783]  __netif_receive_skb_one_core+0x5a/0x80
>>> [60919.609864]  process_backlog+0x9b/0x150
>>> [60919.618691]  net_rx_action+0x156/0x400
>>> [60919.627310]  ? sched_clock_cpu+0xc/0xa0
>>> [60919.635714]  __do_softirq+0xe8/0x2e9
>>> [60919.644161]  do_softirq_own_stack+0x2a/0x40
>>> [60919.652154]  </IRQ>
>>> [60919.659899]  do_softirq.part.17+0x4f/0x60
>>> [60919.667475]  __local_bh_enable_ip+0x60/0x70
>>> [60919.675089]  __dev_queue_xmit+0x539/0x920
>>> [60919.682267]  ? finish_wait+0x80/0x80
>>> [60919.689218]  ? finish_wait+0x80/0x80
>>> [60919.695886]  ? sock_alloc_send_pskb+0x211/0x230
>>> [60919.702395]  ? can_send+0xe5/0x1f0 [can]
>>> [60919.708882]  can_send+0xe5/0x1f0 [can]
>>> [60919.715037]  raw_sendmsg+0x16d/0x268 [can_raw]
>>>
>>> It's because raw_setsockopt() concurrently with
>>> unregister_netdevice_many(). Concurrent scenario as following.
>>>
>>>     cpu0                        cpu1
>>> raw_bind
>>> raw_setsockopt                    unregister_netdevice_many
>>>                         unlist_netdevice
>>> dev_get_by_index                raw_notifier
>>> raw_enable_filters                ......
>>> can_rx_register
>>> can_rcv_list_find(..., net->can.rx_alldev_list)
>>>
>>> ......
>>>
>>> sock_close
>>> raw_release(sock_a)
>>>
>>> ......
>>>
>>> can_receive
>>> can_rcv_filter(net->can.rx_alldev_list, ...)
>>> raw_rcv(skb, sock_a)
>>> BUG
>>>
>>> After unlist_netdevice(), dev_get_by_index() return NULL in
>>> raw_setsockopt(). Function raw_enable_filters() will add sock
>>> and can_filter to net->can.rx_alldev_list.

Btw. this should not happen too!

dev_get_by_index() is executed depending on ro->ifindex which means 
there should be a real network interface. When dev_get_by_index() 
returns NULL this can considered to be wrong.

Adding a new filter to net->can.rx_alldev_list as a consequence is wrong 
too.

>>> Then the sock is closed.
>>> Followed by, we sock_sendmsg() to a new vcan device use the same
>>> can_filter. Protocol stack match the old receiver whose sock has
>>> been released on net->can.rx_alldev_list in can_rcv_filter().
>>> Function raw_rcv() uses the freed sock. UAF BUG is triggered.
>>>
>>> We can find that the key issue is that net_device has not been
>>> protected in raw_setsockopt(). Use rtnl_lock to protect net_device
>>> in raw_setsockopt().
>>>
>>> Fixes: c18ce101f2e4 ("[CAN]: Add raw protocol")
>>> Signed-off-by: Ziyang Xuan <william.xuanziyang@huawei.com>

Can you please resend the below patch as suggested by Greg KH and add my

Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>

as it also adds the dev_get_by_index() return check.

diff --git a/net/can/raw.c b/net/can/raw.c
index ed4fcb7ab0c3..d3cbc32036c7 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -544,14 +544,18 @@ static int raw_setsockopt(struct socket *sock, int 
level, int optname,
  		} else if (count == 1) {
  			if (copy_from_sockptr(&sfilter, optval, sizeof(sfilter)))
  				return -EFAULT;
  		}

+		rtnl_lock();
  		lock_sock(sk);

-		if (ro->bound && ro->ifindex)
+		if (ro->bound && ro->ifindex) {
  			dev = dev_get_by_index(sock_net(sk), ro->ifindex);
+			if (!dev)
+				goto out_fil;
+		}

  		if (ro->bound) {
  			/* (try to) register the new filters */
  			if (count == 1)
  				err = raw_enable_filters(sock_net(sk), dev, sk,
@@ -586,10 +590,11 @@ static int raw_setsockopt(struct socket *sock, int 
level, int optname,
   out_fil:
  		if (dev)
  			dev_put(dev);

  		release_sock(sk);
+		rtnl_unlock();

  		break;

  	case CAN_RAW_ERR_FILTER:
  		if (optlen != sizeof(err_mask))
@@ -598,14 +603,18 @@ static int raw_setsockopt(struct socket *sock, int 
level, int optname,
  		if (copy_from_sockptr(&err_mask, optval, optlen))
  			return -EFAULT;

  		err_mask &= CAN_ERR_MASK;

+		rtnl_lock();
  		lock_sock(sk);

-		if (ro->bound && ro->ifindex)
+		if (ro->bound && ro->ifindex) {
  			dev = dev_get_by_index(sock_net(sk), ro->ifindex);
+			if (!dev)
+				goto out_err;
+		}

  		/* remove current error mask */
  		if (ro->bound) {
  			/* (try to) register the new err_mask */
  			err = raw_enable_errfilter(sock_net(sk), dev, sk,
@@ -625,10 +634,11 @@ static int raw_setsockopt(struct socket *sock, int 
level, int optname,
   out_err:
  		if (dev)
  			dev_put(dev);

  		release_sock(sk);
+		rtnl_unlock();

  		break;

  	case CAN_RAW_LOOPBACK:
  		if (optlen != sizeof(ro->loopback))




Thanks for the finding!

Best regards,
Oliver

(..)
>>
>>
>> <formletter>
>>
>> This is not the correct way to submit patches for inclusion in the
>> stable kernel tree.  Please read:
>>      
>> https://www.kernel.org/doc/html/latest/process/stable-kernel-rules.html
>> for how to do this properly.
>>
>> </formletter>
>>
Ziyang Xuan (William) July 21, 2021, 9:29 a.m. UTC | #4
On 7/21/2021 2:35 PM, Oliver Hartkopp wrote:
> 
> 
> On 21.07.21 06:53, Greg KH wrote:
>> On Wed, Jul 21, 2021 at 09:09:37AM +0800, Ziyang Xuan wrote:
>>> We get a bug during ltp can_filter test as following.
>>>
>>> ===========================================
>>> [60919.264984] BUG: unable to handle kernel NULL pointer dereference at 0000000000000010
>>> [60919.265223] PGD 8000003dda726067 P4D 8000003dda726067 PUD 3dda727067 PMD 0
>>> [60919.265443] Oops: 0000 [#1] SMP PTI
>>> [60919.265550] CPU: 30 PID: 3638365 Comm: can_filter Kdump: loaded Tainted: G        W         4.19.90+ #1
> 
> This kernel version 4.19.90 is definitely outdated.
> 
> Can you please check your issue with the latest uptream kernel as this problem should have been fixed with this patch:
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8d0caedb759683041d9db82069937525999ada53
> ("can: bcm/raw/isotp: use per module netdevice notifier")
> 
> Thanks!

I have tested it under the latest 5.14-rc2 kernel version which includes commit 8d0caedb7596 before I submit the patch.
Although I failed to get the vmcore-dmesg file after updating the kernel version to 5.14-rc2 to display here.
But we can get the conclusion according to the following debug messages and my problem analysis.

==========================================
[ 1048.953574] unlist_netdevice name[vcan0]
[ 1048.953661] raw_notify 283: enter, waiting
[ 1050.950967] raw_setsockopt 552: ro->bound[1] ro->ifindex[8] sk[ffff9420c5699800]
[ 1053.956002] can: receive list entry not found for dev any, id 000, mask 000
[ 1053.961989] can: receive list entry not found for dev vcan0, id 123, mask 7FF

raw_setsockopt() executes after unlist_netdevice() and before raw_notify().
The problem always exists.

> 
>>> [60919.266068] RIP: 0010:selinux_socket_sock_rcv_skb+0x3e/0x200
>>> [60919.293289] RSP: 0018:ffff8d53bfc03cf8 EFLAGS: 00010246
>>> [60919.307140] RAX: 0000000000000000 RBX: 000000000000001d RCX: 0000000000000007
>>> [60919.320756] RDX: 0000000000000001 RSI: ffff8d5104a8ed00 RDI: ffff8d53bfc03d30
>>> [60919.334319] RBP: ffff8d9338056800 R08: ffff8d53bfc29d80 R09: 0000000000000001
>>> [60919.347969] R10: ffff8d53bfc03ec0 R11: ffffb8526ef47c98 R12: ffff8d53bfc03d30
>>> [60919.350320] perf: interrupt took too long (3063 > 2500), lowering kernel.perf_event_max_sample_rate to 65000
>>> [60919.361148] R13: 0000000000000001 R14: ffff8d53bcf90000 R15: 0000000000000000
>>> [60919.361151] FS:  00007fb78b6b3600(0000) GS:ffff8d53bfc00000(0000) knlGS:0000000000000000
>>> [60919.400812] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
>>> [60919.413730] CR2: 0000000000000010 CR3: 0000003e3f784006 CR4: 00000000007606e0
>>> [60919.426479] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
>>> [60919.439339] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
>>> [60919.451608] PKRU: 55555554
>>> [60919.463622] Call Trace:
>>> [60919.475617]  <IRQ>
>>> [60919.487122]  ? update_load_avg+0x89/0x5d0
>>> [60919.498478]  ? update_load_avg+0x89/0x5d0
>>> [60919.509822]  ? account_entity_enqueue+0xc5/0xf0
>>> [60919.520709]  security_sock_rcv_skb+0x2a/0x40
>>> [60919.531413]  sk_filter_trim_cap+0x47/0x1b0
>>> [60919.542178]  ? kmem_cache_alloc+0x38/0x1b0
>>> [60919.552444]  sock_queue_rcv_skb+0x17/0x30
>>> [60919.562477]  raw_rcv+0x110/0x190 [can_raw]
>>> [60919.572539]  can_rcv_filter+0xbc/0x1b0 [can]
>>> [60919.582173]  can_receive+0x6b/0xb0 [can]
>>> [60919.591595]  can_rcv+0x31/0x70 [can]
>>> [60919.600783]  __netif_receive_skb_one_core+0x5a/0x80
>>> [60919.609864]  process_backlog+0x9b/0x150
>>> [60919.618691]  net_rx_action+0x156/0x400
>>> [60919.627310]  ? sched_clock_cpu+0xc/0xa0
>>> [60919.635714]  __do_softirq+0xe8/0x2e9
>>> [60919.644161]  do_softirq_own_stack+0x2a/0x40
>>> [60919.652154]  </IRQ>
>>> [60919.659899]  do_softirq.part.17+0x4f/0x60
>>> [60919.667475]  __local_bh_enable_ip+0x60/0x70
>>> [60919.675089]  __dev_queue_xmit+0x539/0x920
>>> [60919.682267]  ? finish_wait+0x80/0x80
>>> [60919.689218]  ? finish_wait+0x80/0x80
>>> [60919.695886]  ? sock_alloc_send_pskb+0x211/0x230
>>> [60919.702395]  ? can_send+0xe5/0x1f0 [can]
>>> [60919.708882]  can_send+0xe5/0x1f0 [can]
>>> [60919.715037]  raw_sendmsg+0x16d/0x268 [can_raw]
>>>
>>> It's because raw_setsockopt() concurrently with
>>> unregister_netdevice_many(). Concurrent scenario as following.
>>>
>>>     cpu0                        cpu1
>>> raw_bind
>>> raw_setsockopt                    unregister_netdevice_many
>>>                         unlist_netdevice
>>> dev_get_by_index                raw_notifier
>>> raw_enable_filters                ......
>>> can_rx_register
>>> can_rcv_list_find(..., net->can.rx_alldev_list)
>>>
>>> ......
>>>
>>> sock_close
>>> raw_release(sock_a)
>>>
>>> ......
>>>
>>> can_receive
>>> can_rcv_filter(net->can.rx_alldev_list, ...)
>>> raw_rcv(skb, sock_a)
>>> BUG
>>>
>>> After unlist_netdevice(), dev_get_by_index() return NULL in
>>> raw_setsockopt(). Function raw_enable_filters() will add sock
>>> and can_filter to net->can.rx_alldev_list. Then the sock is closed.
>>> Followed by, we sock_sendmsg() to a new vcan device use the same
>>> can_filter. Protocol stack match the old receiver whose sock has
>>> been released on net->can.rx_alldev_list in can_rcv_filter().
>>> Function raw_rcv() uses the freed sock. UAF BUG is triggered.
>>>
>>> We can find that the key issue is that net_device has not been
>>> protected in raw_setsockopt(). Use rtnl_lock to protect net_device
>>> in raw_setsockopt().
>>>
>>> Fixes: c18ce101f2e4 ("[CAN]: Add raw protocol")
>>> Signed-off-by: Ziyang Xuan <william.xuanziyang@huawei.com>
>>> ---
>>>   net/can/raw.c | 4 ++++
>>>   1 file changed, 4 insertions(+)
>>>
>>> diff --git a/net/can/raw.c b/net/can/raw.c
>>> index ed4fcb7ab0c3..a63e9915c66a 100644
>>> --- a/net/can/raw.c
>>> +++ b/net/can/raw.c
>>> @@ -546,6 +546,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>>>                   return -EFAULT;
>>>           }
>>>   +        rtnl_lock();
>>>           lock_sock(sk);
>>>             if (ro->bound && ro->ifindex)
>>> @@ -588,6 +589,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>>>               dev_put(dev);
>>>             release_sock(sk);
>>> +        rtnl_unlock();
>>>             break;
>>>   @@ -600,6 +602,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>>>             err_mask &= CAN_ERR_MASK;
>>>   +        rtnl_lock();
>>>           lock_sock(sk);
>>>             if (ro->bound && ro->ifindex)
>>> @@ -627,6 +630,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>>>               dev_put(dev);
>>>             release_sock(sk);
>>> +        rtnl_unlock();
>>>             break;
>>>   -- 
>>> 2.25.1
>>>
>>
>>
>> <formletter>
>>
>> This is not the correct way to submit patches for inclusion in the
>> stable kernel tree.  Please read:
>>      https://www.kernel.org/doc/html/latest/process/stable-kernel-rules.html
>> for how to do this properly.
>>
>> </formletter>
>>
> .
Oliver Hartkopp July 21, 2021, 9:45 a.m. UTC | #5
On 21.07.21 11:29, Ziyang Xuan (William) wrote:
> On 7/21/2021 2:35 PM, Oliver Hartkopp wrote:
>>
>>
>> On 21.07.21 06:53, Greg KH wrote:
>>> On Wed, Jul 21, 2021 at 09:09:37AM +0800, Ziyang Xuan wrote:
>>>> We get a bug during ltp can_filter test as following.
>>>>
>>>> ===========================================
>>>> [60919.264984] BUG: unable to handle kernel NULL pointer dereference at 0000000000000010
>>>> [60919.265223] PGD 8000003dda726067 P4D 8000003dda726067 PUD 3dda727067 PMD 0
>>>> [60919.265443] Oops: 0000 [#1] SMP PTI
>>>> [60919.265550] CPU: 30 PID: 3638365 Comm: can_filter Kdump: loaded Tainted: G        W         4.19.90+ #1
>>
>> This kernel version 4.19.90 is definitely outdated.
>>
>> Can you please check your issue with the latest uptream kernel as this problem should have been fixed with this patch:
>>
>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8d0caedb759683041d9db82069937525999ada53
>> ("can: bcm/raw/isotp: use per module netdevice notifier")
>>
>> Thanks!
> 
> I have tested it under the latest 5.14-rc2 kernel version which includes commit 8d0caedb7596 before I submit the patch.
> Although I failed to get the vmcore-dmesg file after updating the kernel version to 5.14-rc2 to display here.
> But we can get the conclusion according to the following debug messages and my problem analysis.
> 
> ==========================================
> [ 1048.953574] unlist_netdevice name[vcan0]
> [ 1048.953661] raw_notify 283: enter, waiting
> [ 1050.950967] raw_setsockopt 552: ro->bound[1] ro->ifindex[8] sk[ffff9420c5699800]
> [ 1053.956002] can: receive list entry not found for dev any, id 000, mask 000
> [ 1053.961989] can: receive list entry not found for dev vcan0, id 123, mask 7FF
> 
> raw_setsockopt() executes after unlist_netdevice() and before raw_notify().
> The problem always exists.
> 

You are right!

In the meantime I sent a new reply to your original patch here:

https://lore.kernel.org/linux-can/11822417-5931-b2d8-ae77-ec4a84b8b895@hartkopp.net/

Thanks!
Ziyang Xuan (William) July 21, 2021, 11:37 a.m. UTC | #6
On 7/21/2021 5:24 PM, Oliver Hartkopp wrote:
> Answering myself ...
> 
> On 21.07.21 08:35, Oliver Hartkopp wrote:
>>
>>
>> On 21.07.21 06:53, Greg KH wrote:
>>> On Wed, Jul 21, 2021 at 09:09:37AM +0800, Ziyang Xuan wrote:
>>>> We get a bug during ltp can_filter test as following.
>>>>
>>>> ===========================================
>>>> [60919.264984] BUG: unable to handle kernel NULL pointer dereference at 0000000000000010
>>>> [60919.265223] PGD 8000003dda726067 P4D 8000003dda726067 PUD 3dda727067 PMD 0
>>>> [60919.265443] Oops: 0000 [#1] SMP PTI
>>>> [60919.265550] CPU: 30 PID: 3638365 Comm: can_filter Kdump: loaded Tainted: G        W         4.19.90+ #1
>>
>> This kernel version 4.19.90 is definitely outdated.
>>
>> Can you please check your issue with the latest uptream kernel as this problem should have been fixed with this patch:
>>
>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8d0caedb759683041d9db82069937525999ada53
>> ("can: bcm/raw/isotp: use per module netdevice notifier")
>>
>> Thanks!
> 
> I think my hint had a wrong assumption. The suggestion to add some locking seems correct.
> 
>>>> [60919.266068] RIP: 0010:selinux_socket_sock_rcv_skb+0x3e/0x200
>>>> [60919.293289] RSP: 0018:ffff8d53bfc03cf8 EFLAGS: 00010246
>>>> [60919.307140] RAX: 0000000000000000 RBX: 000000000000001d RCX: 0000000000000007
>>>> [60919.320756] RDX: 0000000000000001 RSI: ffff8d5104a8ed00 RDI: ffff8d53bfc03d30
>>>> [60919.334319] RBP: ffff8d9338056800 R08: ffff8d53bfc29d80 R09: 0000000000000001
>>>> [60919.347969] R10: ffff8d53bfc03ec0 R11: ffffb8526ef47c98 R12: ffff8d53bfc03d30
>>>> [60919.350320] perf: interrupt took too long (3063 > 2500), lowering kernel.perf_event_max_sample_rate to 65000
>>>> [60919.361148] R13: 0000000000000001 R14: ffff8d53bcf90000 R15: 0000000000000000
>>>> [60919.361151] FS:  00007fb78b6b3600(0000) GS:ffff8d53bfc00000(0000) knlGS:0000000000000000
>>>> [60919.400812] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
>>>> [60919.413730] CR2: 0000000000000010 CR3: 0000003e3f784006 CR4: 00000000007606e0
>>>> [60919.426479] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
>>>> [60919.439339] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
>>>> [60919.451608] PKRU: 55555554
>>>> [60919.463622] Call Trace:
>>>> [60919.475617]  <IRQ>
>>>> [60919.487122]  ? update_load_avg+0x89/0x5d0
>>>> [60919.498478]  ? update_load_avg+0x89/0x5d0
>>>> [60919.509822]  ? account_entity_enqueue+0xc5/0xf0
>>>> [60919.520709]  security_sock_rcv_skb+0x2a/0x40
>>>> [60919.531413]  sk_filter_trim_cap+0x47/0x1b0
>>>> [60919.542178]  ? kmem_cache_alloc+0x38/0x1b0
>>>> [60919.552444]  sock_queue_rcv_skb+0x17/0x30
>>>> [60919.562477]  raw_rcv+0x110/0x190 [can_raw]
>>>> [60919.572539]  can_rcv_filter+0xbc/0x1b0 [can]
>>>> [60919.582173]  can_receive+0x6b/0xb0 [can]
>>>> [60919.591595]  can_rcv+0x31/0x70 [can]
>>>> [60919.600783]  __netif_receive_skb_one_core+0x5a/0x80
>>>> [60919.609864]  process_backlog+0x9b/0x150
>>>> [60919.618691]  net_rx_action+0x156/0x400
>>>> [60919.627310]  ? sched_clock_cpu+0xc/0xa0
>>>> [60919.635714]  __do_softirq+0xe8/0x2e9
>>>> [60919.644161]  do_softirq_own_stack+0x2a/0x40
>>>> [60919.652154]  </IRQ>
>>>> [60919.659899]  do_softirq.part.17+0x4f/0x60
>>>> [60919.667475]  __local_bh_enable_ip+0x60/0x70
>>>> [60919.675089]  __dev_queue_xmit+0x539/0x920
>>>> [60919.682267]  ? finish_wait+0x80/0x80
>>>> [60919.689218]  ? finish_wait+0x80/0x80
>>>> [60919.695886]  ? sock_alloc_send_pskb+0x211/0x230
>>>> [60919.702395]  ? can_send+0xe5/0x1f0 [can]
>>>> [60919.708882]  can_send+0xe5/0x1f0 [can]
>>>> [60919.715037]  raw_sendmsg+0x16d/0x268 [can_raw]
>>>>
>>>> It's because raw_setsockopt() concurrently with
>>>> unregister_netdevice_many(). Concurrent scenario as following.
>>>>
>>>>     cpu0                        cpu1
>>>> raw_bind
>>>> raw_setsockopt                    unregister_netdevice_many
>>>>                         unlist_netdevice
>>>> dev_get_by_index                raw_notifier
>>>> raw_enable_filters                ......
>>>> can_rx_register
>>>> can_rcv_list_find(..., net->can.rx_alldev_list)
>>>>
>>>> ......
>>>>
>>>> sock_close
>>>> raw_release(sock_a)
>>>>
>>>> ......
>>>>
>>>> can_receive
>>>> can_rcv_filter(net->can.rx_alldev_list, ...)
>>>> raw_rcv(skb, sock_a)
>>>> BUG
>>>>
>>>> After unlist_netdevice(), dev_get_by_index() return NULL in
>>>> raw_setsockopt(). Function raw_enable_filters() will add sock
>>>> and can_filter to net->can.rx_alldev_list.
> 
> Btw. this should not happen too!
> 
> dev_get_by_index() is executed depending on ro->ifindex which means there should be a real network interface. When dev_get_by_index() returns NULL this can considered to be wrong.
> 
> Adding a new filter to net->can.rx_alldev_list as a consequence is wrong too.
> 
>>>> Then the sock is closed.
>>>> Followed by, we sock_sendmsg() to a new vcan device use the same
>>>> can_filter. Protocol stack match the old receiver whose sock has
>>>> been released on net->can.rx_alldev_list in can_rcv_filter().
>>>> Function raw_rcv() uses the freed sock. UAF BUG is triggered.
>>>>
>>>> We can find that the key issue is that net_device has not been
>>>> protected in raw_setsockopt(). Use rtnl_lock to protect net_device
>>>> in raw_setsockopt().
>>>>
>>>> Fixes: c18ce101f2e4 ("[CAN]: Add raw protocol")
>>>> Signed-off-by: Ziyang Xuan <william.xuanziyang@huawei.com>
> 
> Can you please resend the below patch as suggested by Greg KH and add my
> 
> Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
> 
> as it also adds the dev_get_by_index() return check.
> 
> diff --git a/net/can/raw.c b/net/can/raw.c
> index ed4fcb7ab0c3..d3cbc32036c7 100644
> --- a/net/can/raw.c
> +++ b/net/can/raw.c
> @@ -544,14 +544,18 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>          } else if (count == 1) {
>              if (copy_from_sockptr(&sfilter, optval, sizeof(sfilter)))
>                  return -EFAULT;
>          }
> 
> +        rtnl_lock();
>          lock_sock(sk);
> 
> -        if (ro->bound && ro->ifindex)
> +        if (ro->bound && ro->ifindex) {
>              dev = dev_get_by_index(sock_net(sk), ro->ifindex);
> +            if (!dev)
> +                goto out_fil;
> +        }
At first, I also use this modification. After discussion with my partner, we found that
it is impossible scenario if we use rtnl_lock to protect net_device object.
We can see two sequences:
1. raw_setsockopt first get rtnl_lock, unregister_netdevice_many later.
It can be simplified to add the filter in raw_setsockopt, then remove the filter in raw_notify.

2. unregister_netdevice_many first get rtnl_lock, raw_setsockopt later.
raw_notify will set ro->ifindex, ro->bound and ro->count to zero firstly. The filter will not
be added to any filter_list in raw_notify.

So I selected the current modification. Do you think so?

My first modification as following:

diff --git a/net/can/raw.c b/net/can/raw.c
index ed4fcb7ab0c3..a0ce4908317f 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -546,10 +546,16 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
                                return -EFAULT;
                }

+               rtnl_lock();
                lock_sock(sk);

-               if (ro->bound && ro->ifindex)
+               if (ro->bound && ro->ifindex) {
                        dev = dev_get_by_index(sock_net(sk), ro->ifindex);
+                       if (!dev) {
+                               err = -ENODEV;
+                               goto out_fil;
+                       }
+               }

                if (ro->bound) {
                        /* (try to) register the new filters */
@@ -559,11 +565,8 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
                        else
                                err = raw_enable_filters(sock_net(sk), dev, sk,
                                                         filter, count);
-                       if (err) {
-                               if (count > 1)
-                                       kfree(filter);
+                       if (err)
                                goto out_fil;
-                       }

                        /* remove old filter registrations */
                        raw_disable_filters(sock_net(sk), dev, sk, ro->filter,
@@ -584,10 +587,14 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
                ro->count  = count;

  out_fil:
+               if (err && count > 1)
+                       kfree(filter);
+
                if (dev)
                        dev_put(dev);

                release_sock(sk);
+               rtnl_unlock();

                break;

@@ -600,10 +607,16 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,

                err_mask &= CAN_ERR_MASK;

+               rtnl_lock();
                lock_sock(sk);

-               if (ro->bound && ro->ifindex)
+               if (ro->bound && ro->ifindex) {
                        dev = dev_get_by_index(sock_net(sk), ro->ifindex);
+                       if (!dev) {
+                               err = -ENODEV;
+                               goto out_err;
+                       }
+               }

                /* remove current error mask */
                if (ro->bound) {
@@ -627,6 +640,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
                        dev_put(dev);

                release_sock(sk);
+               rtnl_unlock();

                break;

> 
>          if (ro->bound) {
>              /* (try to) register the new filters */
>              if (count == 1)
>                  err = raw_enable_filters(sock_net(sk), dev, sk,
> @@ -586,10 +590,11 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>   out_fil:
>          if (dev)
>              dev_put(dev);
> 
>          release_sock(sk);
> +        rtnl_unlock();
> 
>          break;
> 
>      case CAN_RAW_ERR_FILTER:
>          if (optlen != sizeof(err_mask))
> @@ -598,14 +603,18 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>          if (copy_from_sockptr(&err_mask, optval, optlen))
>              return -EFAULT;
> 
>          err_mask &= CAN_ERR_MASK;
> 
> +        rtnl_lock();
>          lock_sock(sk);
> 
> -        if (ro->bound && ro->ifindex)
> +        if (ro->bound && ro->ifindex) {
>              dev = dev_get_by_index(sock_net(sk), ro->ifindex);
> +            if (!dev)
> +                goto out_err;
> +        }
> 
>          /* remove current error mask */
>          if (ro->bound) {
>              /* (try to) register the new err_mask */
>              err = raw_enable_errfilter(sock_net(sk), dev, sk,
> @@ -625,10 +634,11 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>   out_err:
>          if (dev)
>              dev_put(dev);
> 
>          release_sock(sk);
> +        rtnl_unlock();
> 
>          break;
> 
>      case CAN_RAW_LOOPBACK:
>          if (optlen != sizeof(ro->loopback))
> 
> 
> 
> 
> Thanks for the finding!
> 
> Best regards,
> Oliver
> 
> (..)
>>>
>>>
>>> <formletter>
>>>
>>> This is not the correct way to submit patches for inclusion in the
>>> stable kernel tree.  Please read:
>>>      https://www.kernel.org/doc/html/latest/process/stable-kernel-rules.html
>>> for how to do this properly.
>>>
>>> </formletter>
>>>
> .
Oliver Hartkopp July 21, 2021, 3:13 p.m. UTC | #7
On 21.07.21 13:37, Ziyang Xuan (William) wrote:
> On 7/21/2021 5:24 PM, Oliver Hartkopp wrote:

>>
>> Can you please resend the below patch as suggested by Greg KH and add my
>>
>> Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
>>
>> as it also adds the dev_get_by_index() return check.
>>
>> diff --git a/net/can/raw.c b/net/can/raw.c
>> index ed4fcb7ab0c3..d3cbc32036c7 100644
>> --- a/net/can/raw.c
>> +++ b/net/can/raw.c
>> @@ -544,14 +544,18 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>>           } else if (count == 1) {
>>               if (copy_from_sockptr(&sfilter, optval, sizeof(sfilter)))
>>                   return -EFAULT;
>>           }
>>
>> +        rtnl_lock();
>>           lock_sock(sk);
>>
>> -        if (ro->bound && ro->ifindex)
>> +        if (ro->bound && ro->ifindex) {
>>               dev = dev_get_by_index(sock_net(sk), ro->ifindex);
>> +            if (!dev)
>> +                goto out_fil;
>> +        }
> At first, I also use this modification. After discussion with my partner, we found that
> it is impossible scenario if we use rtnl_lock to protect net_device object.
> We can see two sequences:
> 1. raw_setsockopt first get rtnl_lock, unregister_netdevice_many later.
> It can be simplified to add the filter in raw_setsockopt, then remove the filter in raw_notify.
> 
> 2. unregister_netdevice_many first get rtnl_lock, raw_setsockopt later.
> raw_notify will set ro->ifindex, ro->bound and ro->count to zero firstly. The filter will not
> be added to any filter_list in raw_notify.
> 
> So I selected the current modification. Do you think so?
> 
> My first modification as following:
> 
> diff --git a/net/can/raw.c b/net/can/raw.c
> index ed4fcb7ab0c3..a0ce4908317f 100644
> --- a/net/can/raw.c
> +++ b/net/can/raw.c
> @@ -546,10 +546,16 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>                                  return -EFAULT;
>                  }
> 
> +               rtnl_lock();
>                  lock_sock(sk);
> 
> -               if (ro->bound && ro->ifindex)
> +               if (ro->bound && ro->ifindex) {
>                          dev = dev_get_by_index(sock_net(sk), ro->ifindex);
> +                       if (!dev) {
> +                               err = -ENODEV;
> +                               goto out_fil;
> +                       }
> +               }
> 
>                  if (ro->bound) {
>                          /* (try to) register the new filters */
> @@ -559,11 +565,8 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>                          else
>                                  err = raw_enable_filters(sock_net(sk), dev, sk,
>                                                           filter, count);
> -                       if (err) {
> -                               if (count > 1)
> -                                       kfree(filter);
> +                       if (err)
>                                  goto out_fil;
> -                       }
> 
>                          /* remove old filter registrations */
>                          raw_disable_filters(sock_net(sk), dev, sk, ro->filter,
> @@ -584,10 +587,14 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>                  ro->count  = count;
> 
>    out_fil:
> +               if (err && count > 1)
> +                       kfree(filter);
> +

Setting the err variable to -ENODEV is a good idea but I do not like the 
movement of kfree(filter).

The kfree() has a tight relation inside the if-statement for ro->bound 
which makes it easier to understand.

Regards,
Oliver

ps. your patches have less context than mine. Do you have different 
settings for -U<n>, --unified=<n> for 'git diff' ?

>                  if (dev)
>                          dev_put(dev);
> 
>                  release_sock(sk);
> +               rtnl_unlock();
> 
>                  break;
> 
> @@ -600,10 +607,16 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
> 
>                  err_mask &= CAN_ERR_MASK;
> 
> +               rtnl_lock();
>                  lock_sock(sk);
> 
> -               if (ro->bound && ro->ifindex)
> +               if (ro->bound && ro->ifindex) {
>                          dev = dev_get_by_index(sock_net(sk), ro->ifindex);
> +                       if (!dev) {
> +                               err = -ENODEV;
> +                               goto out_err;
> +                       }
> +               }
> 
>                  /* remove current error mask */
>                  if (ro->bound) {
> @@ -627,6 +640,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>                          dev_put(dev);
> 
>                  release_sock(sk);
> +               rtnl_unlock();
> 
>                  break;
>
Ziyang Xuan (William) July 22, 2021, 7:06 a.m. UTC | #8
On 7/21/2021 11:13 PM, Oliver Hartkopp wrote:
> 
> 
> On 21.07.21 13:37, Ziyang Xuan (William) wrote:
>> On 7/21/2021 5:24 PM, Oliver Hartkopp wrote:
> 
>>>
>>> Can you please resend the below patch as suggested by Greg KH and add my
>>>
>>> Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
>>>
>>> as it also adds the dev_get_by_index() return check.
>>>
>>> diff --git a/net/can/raw.c b/net/can/raw.c
>>> index ed4fcb7ab0c3..d3cbc32036c7 100644
>>> --- a/net/can/raw.c
>>> +++ b/net/can/raw.c
>>> @@ -544,14 +544,18 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>>>           } else if (count == 1) {
>>>               if (copy_from_sockptr(&sfilter, optval, sizeof(sfilter)))
>>>                   return -EFAULT;
>>>           }
>>>
>>> +        rtnl_lock();
>>>           lock_sock(sk);
>>>
>>> -        if (ro->bound && ro->ifindex)
>>> +        if (ro->bound && ro->ifindex) {
>>>               dev = dev_get_by_index(sock_net(sk), ro->ifindex);
>>> +            if (!dev)
>>> +                goto out_fil;
>>> +        }
>> At first, I also use this modification. After discussion with my partner, we found that
>> it is impossible scenario if we use rtnl_lock to protect net_device object.
>> We can see two sequences:
>> 1. raw_setsockopt first get rtnl_lock, unregister_netdevice_many later.
>> It can be simplified to add the filter in raw_setsockopt, then remove the filter in raw_notify.
>>
>> 2. unregister_netdevice_many first get rtnl_lock, raw_setsockopt later.
>> raw_notify will set ro->ifindex, ro->bound and ro->count to zero firstly. The filter will not
>> be added to any filter_list in raw_notify.
>>
>> So I selected the current modification. Do you think so?
>>
>> My first modification as following:
>>
>> diff --git a/net/can/raw.c b/net/can/raw.c
>> index ed4fcb7ab0c3..a0ce4908317f 100644
>> --- a/net/can/raw.c
>> +++ b/net/can/raw.c
>> @@ -546,10 +546,16 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>>                                  return -EFAULT;
>>                  }
>>
>> +               rtnl_lock();
>>                  lock_sock(sk);
>>
>> -               if (ro->bound && ro->ifindex)
>> +               if (ro->bound && ro->ifindex) {
>>                          dev = dev_get_by_index(sock_net(sk), ro->ifindex);
>> +                       if (!dev) {
>> +                               err = -ENODEV;
>> +                               goto out_fil;
>> +                       }
>> +               }
>>
>>                  if (ro->bound) {
>>                          /* (try to) register the new filters */
>> @@ -559,11 +565,8 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>>                          else
>>                                  err = raw_enable_filters(sock_net(sk), dev, sk,
>>                                                           filter, count);
>> -                       if (err) {
>> -                               if (count > 1)
>> -                                       kfree(filter);
>> +                       if (err)
>>                                  goto out_fil;
>> -                       }
>>
>>                          /* remove old filter registrations */
>>                          raw_disable_filters(sock_net(sk), dev, sk, ro->filter,
>> @@ -584,10 +587,14 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
>>                  ro->count  = count;
>>
>>    out_fil:
>> +               if (err && count > 1)
>> +                       kfree(filter);
>> +
> 
> Setting the err variable to -ENODEV is a good idea but I do not like the movement of kfree(filter).
> 
> The kfree() has a tight relation inside the if-statement for ro->bound which makes it easier to understand.
> 
> Regards,
> Oliver

I will submit the v2 patch for the problem according to your suggestions. Than you.
diff mbox series

Patch

diff --git a/net/can/raw.c b/net/can/raw.c
index ed4fcb7ab0c3..a63e9915c66a 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -546,6 +546,7 @@  static int raw_setsockopt(struct socket *sock, int level, int optname,
 				return -EFAULT;
 		}
 
+		rtnl_lock();
 		lock_sock(sk);
 
 		if (ro->bound && ro->ifindex)
@@ -588,6 +589,7 @@  static int raw_setsockopt(struct socket *sock, int level, int optname,
 			dev_put(dev);
 
 		release_sock(sk);
+		rtnl_unlock();
 
 		break;
 
@@ -600,6 +602,7 @@  static int raw_setsockopt(struct socket *sock, int level, int optname,
 
 		err_mask &= CAN_ERR_MASK;
 
+		rtnl_lock();
 		lock_sock(sk);
 
 		if (ro->bound && ro->ifindex)
@@ -627,6 +630,7 @@  static int raw_setsockopt(struct socket *sock, int level, int optname,
 			dev_put(dev);
 
 		release_sock(sk);
+		rtnl_unlock();
 
 		break;