2019-05-19 21:51:43 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2011-12-14 23:43:12 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2011 Intel Corporation. All rights reserved.
|
2014-10-21 22:52:50 +08:00
|
|
|
* Copyright (C) 2014 Marvell International Ltd.
|
2011-12-14 23:43:12 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#define pr_fmt(fmt) "llcp: %s: " fmt, __func__
|
|
|
|
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/nfc.h>
|
|
|
|
|
2013-04-26 17:49:40 +08:00
|
|
|
#include "nfc.h"
|
2011-12-14 23:43:12 +08:00
|
|
|
#include "llcp.h"
|
|
|
|
|
|
|
|
static u8 llcp_magic[3] = {0x46, 0x66, 0x6d};
|
|
|
|
|
2014-02-22 22:14:18 +08:00
|
|
|
static LIST_HEAD(llcp_devices);
|
net: nfc: Fix use-after-free caused by nfc_llcp_find_local
This commit fixes several use-after-free that caused by function
nfc_llcp_find_local(). For example, one UAF can happen when below buggy
time window occurs.
// nfc_genl_llc_get_params | // nfc_unregister_device
|
dev = nfc_get_device(idx); | device_lock(...)
if (!dev) | dev->shutting_down = true;
return -ENODEV; | device_unlock(...);
|
device_lock(...); | // nfc_llcp_unregister_device
| nfc_llcp_find_local()
nfc_llcp_find_local(...); |
| local_cleanup()
if (!local) { |
rc = -ENODEV; | // nfc_llcp_local_put
goto exit; | kref_put(.., local_release)
} |
| // local_release
| list_del(&local->list)
// nfc_genl_send_params | kfree()
local->dev->idx !!!UAF!!! |
|
and the crash trace for the one of the discussed UAF like:
BUG: KASAN: slab-use-after-free in nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
Read of size 8 at addr ffff888105b0e410 by task 20114
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:319 [inline]
print_report+0xcc/0x620 mm/kasan/report.c:430
kasan_report+0xb2/0xe0 mm/kasan/report.c:536
nfc_genl_send_params net/nfc/netlink.c:999 [inline]
nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
genl_family_rcv_msg_doit.isra.0+0x1ee/0x2e0 net/netlink/genetlink.c:968
genl_family_rcv_msg net/netlink/genetlink.c:1048 [inline]
genl_rcv_msg+0x503/0x7d0 net/netlink/genetlink.c:1065
netlink_rcv_skb+0x161/0x430 net/netlink/af_netlink.c:2548
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1076
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x644/0x900 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x934/0xe70 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg+0x1b6/0x200 net/socket.c:747
____sys_sendmsg+0x6e9/0x890 net/socket.c:2501
___sys_sendmsg+0x110/0x1b0 net/socket.c:2555
__sys_sendmsg+0xf7/0x1d0 net/socket.c:2584
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
RIP: 0033:0x7f34640a2389
RSP: 002b:00007f3463415168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f34641c1f80 RCX: 00007f34640a2389
RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000006
RBP: 00007f34640ed493 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffe38449ecf R14: 00007f3463415300 R15: 0000000000022000
</TASK>
Allocated by task 20116:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
____kasan_kmalloc mm/kasan/common.c:374 [inline]
__kasan_kmalloc+0x7f/0x90 mm/kasan/common.c:383
kmalloc include/linux/slab.h:580 [inline]
kzalloc include/linux/slab.h:720 [inline]
nfc_llcp_register_device+0x49/0xa40 net/nfc/llcp_core.c:1567
nfc_register_device+0x61/0x260 net/nfc/core.c:1124
nci_register_device+0x776/0xb20 net/nfc/nci/core.c:1257
virtual_ncidev_open+0x147/0x230 drivers/nfc/virtual_ncidev.c:148
misc_open+0x379/0x4a0 drivers/char/misc.c:165
chrdev_open+0x26c/0x780 fs/char_dev.c:414
do_dentry_open+0x6c4/0x12a0 fs/open.c:920
do_open fs/namei.c:3560 [inline]
path_openat+0x24fe/0x37e0 fs/namei.c:3715
do_filp_open+0x1ba/0x410 fs/namei.c:3742
do_sys_openat2+0x171/0x4c0 fs/open.c:1356
do_sys_open fs/open.c:1372 [inline]
__do_sys_openat fs/open.c:1388 [inline]
__se_sys_openat fs/open.c:1383 [inline]
__x64_sys_openat+0x143/0x200 fs/open.c:1383
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Freed by task 20115:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521
____kasan_slab_free mm/kasan/common.c:236 [inline]
____kasan_slab_free mm/kasan/common.c:200 [inline]
__kasan_slab_free+0x10a/0x190 mm/kasan/common.c:244
kasan_slab_free include/linux/kasan.h:162 [inline]
slab_free_hook mm/slub.c:1781 [inline]
slab_free_freelist_hook mm/slub.c:1807 [inline]
slab_free mm/slub.c:3787 [inline]
__kmem_cache_free+0x7a/0x190 mm/slub.c:3800
local_release net/nfc/llcp_core.c:174 [inline]
kref_put include/linux/kref.h:65 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:182 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:177 [inline]
nfc_llcp_unregister_device+0x206/0x290 net/nfc/llcp_core.c:1620
nfc_unregister_device+0x160/0x1d0 net/nfc/core.c:1179
virtual_ncidev_close+0x52/0xa0 drivers/nfc/virtual_ncidev.c:163
__fput+0x252/0xa20 fs/file_table.c:321
task_work_run+0x174/0x270 kernel/task_work.c:179
resume_user_mode_work include/linux/resume_user_mode.h:49 [inline]
exit_to_user_mode_loop kernel/entry/common.c:171 [inline]
exit_to_user_mode_prepare+0x108/0x110 kernel/entry/common.c:204
__syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline]
syscall_exit_to_user_mode+0x21/0x50 kernel/entry/common.c:297
do_syscall_64+0x4c/0x90 arch/x86/entry/common.c:86
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Last potentially related work creation:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
__kasan_record_aux_stack+0x95/0xb0 mm/kasan/generic.c:491
kvfree_call_rcu+0x29/0xa80 kernel/rcu/tree.c:3328
drop_sysctl_table+0x3be/0x4e0 fs/proc/proc_sysctl.c:1735
unregister_sysctl_table.part.0+0x9c/0x190 fs/proc/proc_sysctl.c:1773
unregister_sysctl_table+0x24/0x30 fs/proc/proc_sysctl.c:1753
neigh_sysctl_unregister+0x5f/0x80 net/core/neighbour.c:3895
addrconf_notify+0x140/0x17b0 net/ipv6/addrconf.c:3684
notifier_call_chain+0xbe/0x210 kernel/notifier.c:87
call_netdevice_notifiers_info+0xb5/0x150 net/core/dev.c:1937
call_netdevice_notifiers_extack net/core/dev.c:1975 [inline]
call_netdevice_notifiers net/core/dev.c:1989 [inline]
dev_change_name+0x3c3/0x870 net/core/dev.c:1211
dev_ifsioc+0x800/0xf70 net/core/dev_ioctl.c:376
dev_ioctl+0x3d9/0xf80 net/core/dev_ioctl.c:542
sock_do_ioctl+0x160/0x260 net/socket.c:1213
sock_ioctl+0x3f9/0x670 net/socket.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:870 [inline]
__se_sys_ioctl fs/ioctl.c:856 [inline]
__x64_sys_ioctl+0x19e/0x210 fs/ioctl.c:856
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
The buggy address belongs to the object at ffff888105b0e400
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 16 bytes inside of
freed 1024-byte region [ffff888105b0e400, ffff888105b0e800)
The buggy address belongs to the physical page:
head:ffffea000416c200 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000010200(slab|head|node=0|zone=2)
raw: 0200000000010200 ffff8881000430c0 ffffea00044c7010 ffffea0004510e10
raw: 0000000000000000 00000000000a000a 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888105b0e300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888105b0e380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888105b0e400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888105b0e480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888105b0e500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
In summary, this patch solves those use-after-free by
1. Re-implement the nfc_llcp_find_local(). The current version does not
grab the reference when getting the local from the linked list. For
example, the llcp_sock_bind() gets the reference like below:
// llcp_sock_bind()
local = nfc_llcp_find_local(dev); // A
..... \
| raceable
..... /
llcp_sock->local = nfc_llcp_local_get(local); // B
There is an apparent race window that one can drop the reference
and free the local object fetched in (A) before (B) gets the reference.
2. Some callers of the nfc_llcp_find_local() do not grab the reference
at all. For example, the nfc_genl_llc_{{get/set}_params/sdreq} functions.
We add the nfc_llcp_local_put() for them. Moreover, we add the necessary
error handling function to put the reference.
3. Add the nfc_llcp_remove_local() helper. The local object is removed
from the linked list in local_release() when all reference is gone. This
patch removes it when nfc_llcp_unregister_device() is called.
Therefore, every caller of nfc_llcp_find_local() will get a reference
even when the nfc_llcp_unregister_device() is called. This promises no
use-after-free for the local object is ever possible.
Fixes: 52feb444a903 ("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support")
Fixes: c7aa12252f51 ("NFC: Take a reference on the LLCP local pointer when creating a socket")
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-06-25 17:10:07 +08:00
|
|
|
/* Protects llcp_devices list */
|
|
|
|
static DEFINE_SPINLOCK(llcp_devices_lock);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2013-03-29 18:47:43 +08:00
|
|
|
static void nfc_llcp_rx_skb(struct nfc_llcp_local *local, struct sk_buff *skb);
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
void nfc_llcp_sock_link(struct llcp_sock_list *l, struct sock *sk)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
2012-05-04 23:04:19 +08:00
|
|
|
write_lock(&l->lock);
|
|
|
|
sk_add_node(sk, &l->head);
|
|
|
|
write_unlock(&l->lock);
|
|
|
|
}
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
void nfc_llcp_sock_unlink(struct llcp_sock_list *l, struct sock *sk)
|
|
|
|
{
|
|
|
|
write_lock(&l->lock);
|
|
|
|
sk_del_node_init(sk);
|
|
|
|
write_unlock(&l->lock);
|
|
|
|
}
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2013-04-02 16:25:15 +08:00
|
|
|
void nfc_llcp_socket_remote_param_init(struct nfc_llcp_sock *sock)
|
|
|
|
{
|
|
|
|
sock->remote_rw = LLCP_DEFAULT_RW;
|
|
|
|
sock->remote_miu = LLCP_MAX_MIU + 1;
|
|
|
|
}
|
|
|
|
|
2012-10-27 00:20:10 +08:00
|
|
|
static void nfc_llcp_socket_purge(struct nfc_llcp_sock *sock)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local = sock->local;
|
|
|
|
struct sk_buff *s, *tmp;
|
|
|
|
|
|
|
|
skb_queue_purge(&sock->tx_queue);
|
|
|
|
skb_queue_purge(&sock->tx_pending_queue);
|
|
|
|
|
|
|
|
if (local == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Search for local pending SKBs that are related to this socket */
|
|
|
|
skb_queue_walk_safe(&local->tx_queue, s, tmp) {
|
|
|
|
if (s->sk != &sock->sk)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
skb_unlink(s, &local->tx_queue);
|
|
|
|
kfree_skb(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-03 22:34:19 +08:00
|
|
|
static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool device,
|
2013-02-22 00:01:06 +08:00
|
|
|
int err)
|
2012-05-04 23:04:19 +08:00
|
|
|
{
|
|
|
|
struct sock *sk;
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
struct hlist_node *tmp;
|
2012-05-04 23:04:19 +08:00
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-10-27 00:20:10 +08:00
|
|
|
skb_queue_purge(&local->tx_queue);
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
write_lock(&local->sockets.lock);
|
2012-03-05 08:03:51 +08:00
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
sk_for_each_safe(sk, tmp, &local->sockets.head) {
|
2012-05-04 23:04:19 +08:00
|
|
|
llcp_sock = nfc_llcp_sock(sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-09-26 20:22:10 +08:00
|
|
|
bh_lock_sock(sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-10-27 00:20:10 +08:00
|
|
|
nfc_llcp_socket_purge(llcp_sock);
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
if (sk->sk_state == LLCP_CONNECTED)
|
|
|
|
nfc_put_device(llcp_sock->dev);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
if (sk->sk_state == LLCP_LISTEN) {
|
2011-12-14 23:43:12 +08:00
|
|
|
struct nfc_llcp_sock *lsk, *n;
|
|
|
|
struct sock *accept_sk;
|
|
|
|
|
2012-10-17 21:23:39 +08:00
|
|
|
list_for_each_entry_safe(lsk, n,
|
|
|
|
&llcp_sock->accept_queue,
|
2012-03-05 08:03:52 +08:00
|
|
|
accept_queue) {
|
2011-12-14 23:43:12 +08:00
|
|
|
accept_sk = &lsk->sk;
|
2012-09-26 20:22:10 +08:00
|
|
|
bh_lock_sock(accept_sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
nfc_llcp_accept_unlink(accept_sk);
|
|
|
|
|
2013-02-22 00:01:06 +08:00
|
|
|
if (err)
|
|
|
|
accept_sk->sk_err = err;
|
2011-12-14 23:43:12 +08:00
|
|
|
accept_sk->sk_state = LLCP_CLOSED;
|
2013-02-22 00:01:06 +08:00
|
|
|
accept_sk->sk_state_change(sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-09-26 20:22:10 +08:00
|
|
|
bh_unlock_sock(accept_sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
2012-10-15 21:09:52 +08:00
|
|
|
}
|
|
|
|
|
2013-02-22 00:01:06 +08:00
|
|
|
if (err)
|
|
|
|
sk->sk_err = err;
|
2012-05-04 23:04:19 +08:00
|
|
|
sk->sk_state = LLCP_CLOSED;
|
2013-02-22 00:01:06 +08:00
|
|
|
sk->sk_state_change(sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-09-26 20:22:10 +08:00
|
|
|
bh_unlock_sock(sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
sk_del_node_init(sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
write_unlock(&local->sockets.lock);
|
2013-02-21 23:33:30 +08:00
|
|
|
|
2013-04-03 22:34:19 +08:00
|
|
|
/* If we still have a device, we keep the RAW sockets alive */
|
|
|
|
if (device == true)
|
2013-02-21 23:33:30 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
write_lock(&local->raw_sockets.lock);
|
|
|
|
|
|
|
|
sk_for_each_safe(sk, tmp, &local->raw_sockets.head) {
|
|
|
|
llcp_sock = nfc_llcp_sock(sk);
|
|
|
|
|
|
|
|
bh_lock_sock(sk);
|
|
|
|
|
|
|
|
nfc_llcp_socket_purge(llcp_sock);
|
|
|
|
|
2013-02-22 00:01:06 +08:00
|
|
|
if (err)
|
|
|
|
sk->sk_err = err;
|
2013-02-21 23:33:30 +08:00
|
|
|
sk->sk_state = LLCP_CLOSED;
|
|
|
|
sk->sk_state_change(sk);
|
|
|
|
|
|
|
|
bh_unlock_sock(sk);
|
|
|
|
|
|
|
|
sk_del_node_init(sk);
|
|
|
|
}
|
|
|
|
|
|
|
|
write_unlock(&local->raw_sockets.lock);
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
net: nfc: Fix use-after-free caused by nfc_llcp_find_local
This commit fixes several use-after-free that caused by function
nfc_llcp_find_local(). For example, one UAF can happen when below buggy
time window occurs.
// nfc_genl_llc_get_params | // nfc_unregister_device
|
dev = nfc_get_device(idx); | device_lock(...)
if (!dev) | dev->shutting_down = true;
return -ENODEV; | device_unlock(...);
|
device_lock(...); | // nfc_llcp_unregister_device
| nfc_llcp_find_local()
nfc_llcp_find_local(...); |
| local_cleanup()
if (!local) { |
rc = -ENODEV; | // nfc_llcp_local_put
goto exit; | kref_put(.., local_release)
} |
| // local_release
| list_del(&local->list)
// nfc_genl_send_params | kfree()
local->dev->idx !!!UAF!!! |
|
and the crash trace for the one of the discussed UAF like:
BUG: KASAN: slab-use-after-free in nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
Read of size 8 at addr ffff888105b0e410 by task 20114
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:319 [inline]
print_report+0xcc/0x620 mm/kasan/report.c:430
kasan_report+0xb2/0xe0 mm/kasan/report.c:536
nfc_genl_send_params net/nfc/netlink.c:999 [inline]
nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
genl_family_rcv_msg_doit.isra.0+0x1ee/0x2e0 net/netlink/genetlink.c:968
genl_family_rcv_msg net/netlink/genetlink.c:1048 [inline]
genl_rcv_msg+0x503/0x7d0 net/netlink/genetlink.c:1065
netlink_rcv_skb+0x161/0x430 net/netlink/af_netlink.c:2548
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1076
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x644/0x900 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x934/0xe70 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg+0x1b6/0x200 net/socket.c:747
____sys_sendmsg+0x6e9/0x890 net/socket.c:2501
___sys_sendmsg+0x110/0x1b0 net/socket.c:2555
__sys_sendmsg+0xf7/0x1d0 net/socket.c:2584
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
RIP: 0033:0x7f34640a2389
RSP: 002b:00007f3463415168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f34641c1f80 RCX: 00007f34640a2389
RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000006
RBP: 00007f34640ed493 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffe38449ecf R14: 00007f3463415300 R15: 0000000000022000
</TASK>
Allocated by task 20116:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
____kasan_kmalloc mm/kasan/common.c:374 [inline]
__kasan_kmalloc+0x7f/0x90 mm/kasan/common.c:383
kmalloc include/linux/slab.h:580 [inline]
kzalloc include/linux/slab.h:720 [inline]
nfc_llcp_register_device+0x49/0xa40 net/nfc/llcp_core.c:1567
nfc_register_device+0x61/0x260 net/nfc/core.c:1124
nci_register_device+0x776/0xb20 net/nfc/nci/core.c:1257
virtual_ncidev_open+0x147/0x230 drivers/nfc/virtual_ncidev.c:148
misc_open+0x379/0x4a0 drivers/char/misc.c:165
chrdev_open+0x26c/0x780 fs/char_dev.c:414
do_dentry_open+0x6c4/0x12a0 fs/open.c:920
do_open fs/namei.c:3560 [inline]
path_openat+0x24fe/0x37e0 fs/namei.c:3715
do_filp_open+0x1ba/0x410 fs/namei.c:3742
do_sys_openat2+0x171/0x4c0 fs/open.c:1356
do_sys_open fs/open.c:1372 [inline]
__do_sys_openat fs/open.c:1388 [inline]
__se_sys_openat fs/open.c:1383 [inline]
__x64_sys_openat+0x143/0x200 fs/open.c:1383
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Freed by task 20115:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521
____kasan_slab_free mm/kasan/common.c:236 [inline]
____kasan_slab_free mm/kasan/common.c:200 [inline]
__kasan_slab_free+0x10a/0x190 mm/kasan/common.c:244
kasan_slab_free include/linux/kasan.h:162 [inline]
slab_free_hook mm/slub.c:1781 [inline]
slab_free_freelist_hook mm/slub.c:1807 [inline]
slab_free mm/slub.c:3787 [inline]
__kmem_cache_free+0x7a/0x190 mm/slub.c:3800
local_release net/nfc/llcp_core.c:174 [inline]
kref_put include/linux/kref.h:65 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:182 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:177 [inline]
nfc_llcp_unregister_device+0x206/0x290 net/nfc/llcp_core.c:1620
nfc_unregister_device+0x160/0x1d0 net/nfc/core.c:1179
virtual_ncidev_close+0x52/0xa0 drivers/nfc/virtual_ncidev.c:163
__fput+0x252/0xa20 fs/file_table.c:321
task_work_run+0x174/0x270 kernel/task_work.c:179
resume_user_mode_work include/linux/resume_user_mode.h:49 [inline]
exit_to_user_mode_loop kernel/entry/common.c:171 [inline]
exit_to_user_mode_prepare+0x108/0x110 kernel/entry/common.c:204
__syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline]
syscall_exit_to_user_mode+0x21/0x50 kernel/entry/common.c:297
do_syscall_64+0x4c/0x90 arch/x86/entry/common.c:86
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Last potentially related work creation:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
__kasan_record_aux_stack+0x95/0xb0 mm/kasan/generic.c:491
kvfree_call_rcu+0x29/0xa80 kernel/rcu/tree.c:3328
drop_sysctl_table+0x3be/0x4e0 fs/proc/proc_sysctl.c:1735
unregister_sysctl_table.part.0+0x9c/0x190 fs/proc/proc_sysctl.c:1773
unregister_sysctl_table+0x24/0x30 fs/proc/proc_sysctl.c:1753
neigh_sysctl_unregister+0x5f/0x80 net/core/neighbour.c:3895
addrconf_notify+0x140/0x17b0 net/ipv6/addrconf.c:3684
notifier_call_chain+0xbe/0x210 kernel/notifier.c:87
call_netdevice_notifiers_info+0xb5/0x150 net/core/dev.c:1937
call_netdevice_notifiers_extack net/core/dev.c:1975 [inline]
call_netdevice_notifiers net/core/dev.c:1989 [inline]
dev_change_name+0x3c3/0x870 net/core/dev.c:1211
dev_ifsioc+0x800/0xf70 net/core/dev_ioctl.c:376
dev_ioctl+0x3d9/0xf80 net/core/dev_ioctl.c:542
sock_do_ioctl+0x160/0x260 net/socket.c:1213
sock_ioctl+0x3f9/0x670 net/socket.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:870 [inline]
__se_sys_ioctl fs/ioctl.c:856 [inline]
__x64_sys_ioctl+0x19e/0x210 fs/ioctl.c:856
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
The buggy address belongs to the object at ffff888105b0e400
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 16 bytes inside of
freed 1024-byte region [ffff888105b0e400, ffff888105b0e800)
The buggy address belongs to the physical page:
head:ffffea000416c200 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000010200(slab|head|node=0|zone=2)
raw: 0200000000010200 ffff8881000430c0 ffffea00044c7010 ffffea0004510e10
raw: 0000000000000000 00000000000a000a 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888105b0e300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888105b0e380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888105b0e400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888105b0e480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888105b0e500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
In summary, this patch solves those use-after-free by
1. Re-implement the nfc_llcp_find_local(). The current version does not
grab the reference when getting the local from the linked list. For
example, the llcp_sock_bind() gets the reference like below:
// llcp_sock_bind()
local = nfc_llcp_find_local(dev); // A
..... \
| raceable
..... /
llcp_sock->local = nfc_llcp_local_get(local); // B
There is an apparent race window that one can drop the reference
and free the local object fetched in (A) before (B) gets the reference.
2. Some callers of the nfc_llcp_find_local() do not grab the reference
at all. For example, the nfc_genl_llc_{{get/set}_params/sdreq} functions.
We add the nfc_llcp_local_put() for them. Moreover, we add the necessary
error handling function to put the reference.
3. Add the nfc_llcp_remove_local() helper. The local object is removed
from the linked list in local_release() when all reference is gone. This
patch removes it when nfc_llcp_unregister_device() is called.
Therefore, every caller of nfc_llcp_find_local() will get a reference
even when the nfc_llcp_unregister_device() is called. This promises no
use-after-free for the local object is ever possible.
Fixes: 52feb444a903 ("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support")
Fixes: c7aa12252f51 ("NFC: Take a reference on the LLCP local pointer when creating a socket")
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-06-25 17:10:07 +08:00
|
|
|
static struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local)
|
2012-05-04 17:24:16 +08:00
|
|
|
{
|
|
|
|
kref_get(&local->ref);
|
|
|
|
|
|
|
|
return local;
|
|
|
|
}
|
|
|
|
|
2013-04-03 22:40:52 +08:00
|
|
|
static void local_cleanup(struct nfc_llcp_local *local)
|
2012-05-04 17:24:16 +08:00
|
|
|
{
|
2013-04-03 22:40:52 +08:00
|
|
|
nfc_llcp_socket_release(local, false, ENXIO);
|
2012-05-04 17:24:16 +08:00
|
|
|
del_timer_sync(&local->link_timer);
|
|
|
|
skb_queue_purge(&local->tx_queue);
|
2012-08-23 07:22:16 +08:00
|
|
|
cancel_work_sync(&local->tx_work);
|
|
|
|
cancel_work_sync(&local->rx_work);
|
|
|
|
cancel_work_sync(&local->timeout_work);
|
2012-05-04 17:24:16 +08:00
|
|
|
kfree_skb(local->rx_pending);
|
2023-01-11 21:19:14 +08:00
|
|
|
local->rx_pending = NULL;
|
2013-03-04 22:43:32 +08:00
|
|
|
del_timer_sync(&local->sdreq_timer);
|
|
|
|
cancel_work_sync(&local->sdreq_timeout_work);
|
2013-02-15 17:43:06 +08:00
|
|
|
nfc_llcp_free_sdp_tlv_list(&local->pending_sdreqs);
|
2013-02-21 22:40:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void local_release(struct kref *ref)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local;
|
|
|
|
|
|
|
|
local = container_of(ref, struct nfc_llcp_local, ref);
|
|
|
|
|
2013-04-03 22:40:52 +08:00
|
|
|
local_cleanup(local);
|
2012-05-04 17:24:16 +08:00
|
|
|
kfree(local);
|
|
|
|
}
|
|
|
|
|
|
|
|
int nfc_llcp_local_put(struct nfc_llcp_local *local)
|
|
|
|
{
|
2012-05-04 23:04:19 +08:00
|
|
|
if (local == NULL)
|
|
|
|
return 0;
|
|
|
|
|
2012-05-04 17:24:16 +08:00
|
|
|
return kref_put(&local->ref, local_release);
|
|
|
|
}
|
|
|
|
|
2012-06-25 21:46:28 +08:00
|
|
|
static struct nfc_llcp_sock *nfc_llcp_sock_get(struct nfc_llcp_local *local,
|
|
|
|
u8 ssap, u8 dsap)
|
|
|
|
{
|
|
|
|
struct sock *sk;
|
2012-10-17 03:15:59 +08:00
|
|
|
struct nfc_llcp_sock *llcp_sock, *tmp_sock;
|
2012-06-25 21:46:28 +08:00
|
|
|
|
|
|
|
pr_debug("ssap dsap %d %d\n", ssap, dsap);
|
|
|
|
|
|
|
|
if (ssap == 0 && dsap == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
read_lock(&local->sockets.lock);
|
|
|
|
|
|
|
|
llcp_sock = NULL;
|
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
sk_for_each(sk, &local->sockets.head) {
|
2012-10-17 03:15:59 +08:00
|
|
|
tmp_sock = nfc_llcp_sock(sk);
|
2012-06-25 21:46:28 +08:00
|
|
|
|
2012-10-17 03:15:59 +08:00
|
|
|
if (tmp_sock->ssap == ssap && tmp_sock->dsap == dsap) {
|
|
|
|
llcp_sock = tmp_sock;
|
2023-10-09 20:31:10 +08:00
|
|
|
sock_hold(&llcp_sock->sk);
|
2012-06-25 21:46:28 +08:00
|
|
|
break;
|
2012-10-17 03:15:59 +08:00
|
|
|
}
|
2012-06-25 21:46:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
read_unlock(&local->sockets.lock);
|
|
|
|
|
|
|
|
return llcp_sock;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfc_llcp_sock_put(struct nfc_llcp_sock *sock)
|
|
|
|
{
|
|
|
|
sock_put(&sock->sk);
|
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
static void nfc_llcp_timeout_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
|
2012-03-05 08:03:52 +08:00
|
|
|
timeout_work);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
nfc_dep_link_down(local->dev);
|
|
|
|
}
|
|
|
|
|
2017-10-11 18:33:44 +08:00
|
|
|
static void nfc_llcp_symm_timer(struct timer_list *t)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
2017-10-11 18:33:44 +08:00
|
|
|
struct nfc_llcp_local *local = from_timer(local, t, link_timer);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
pr_err("SYMM timeout\n");
|
|
|
|
|
2012-10-03 07:01:31 +08:00
|
|
|
schedule_work(&local->timeout_work);
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
2013-03-04 22:43:32 +08:00
|
|
|
static void nfc_llcp_sdreq_timeout_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
unsigned long time;
|
|
|
|
HLIST_HEAD(nl_sdres_list);
|
|
|
|
struct hlist_node *n;
|
|
|
|
struct nfc_llcp_sdp_tlv *sdp;
|
|
|
|
struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
|
|
|
|
sdreq_timeout_work);
|
|
|
|
|
|
|
|
mutex_lock(&local->sdreq_lock);
|
|
|
|
|
|
|
|
time = jiffies - msecs_to_jiffies(3 * local->remote_lto);
|
|
|
|
|
|
|
|
hlist_for_each_entry_safe(sdp, n, &local->pending_sdreqs, node) {
|
|
|
|
if (time_after(sdp->time, time))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sdp->sap = LLCP_SDP_UNBOUND;
|
|
|
|
|
|
|
|
hlist_del(&sdp->node);
|
|
|
|
|
|
|
|
hlist_add_head(&sdp->node, &nl_sdres_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hlist_empty(&local->pending_sdreqs))
|
|
|
|
mod_timer(&local->sdreq_timer,
|
|
|
|
jiffies + msecs_to_jiffies(3 * local->remote_lto));
|
|
|
|
|
|
|
|
mutex_unlock(&local->sdreq_lock);
|
|
|
|
|
|
|
|
if (!hlist_empty(&nl_sdres_list))
|
|
|
|
nfc_genl_llc_send_sdres(local->dev, &nl_sdres_list);
|
|
|
|
}
|
|
|
|
|
2017-10-11 18:33:44 +08:00
|
|
|
static void nfc_llcp_sdreq_timer(struct timer_list *t)
|
2013-03-04 22:43:32 +08:00
|
|
|
{
|
2017-10-11 18:33:44 +08:00
|
|
|
struct nfc_llcp_local *local = from_timer(local, t, sdreq_timer);
|
2013-03-04 22:43:32 +08:00
|
|
|
|
|
|
|
schedule_work(&local->sdreq_timeout_work);
|
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev)
|
|
|
|
{
|
2014-02-26 10:26:45 +08:00
|
|
|
struct nfc_llcp_local *local;
|
net: nfc: Fix use-after-free caused by nfc_llcp_find_local
This commit fixes several use-after-free that caused by function
nfc_llcp_find_local(). For example, one UAF can happen when below buggy
time window occurs.
// nfc_genl_llc_get_params | // nfc_unregister_device
|
dev = nfc_get_device(idx); | device_lock(...)
if (!dev) | dev->shutting_down = true;
return -ENODEV; | device_unlock(...);
|
device_lock(...); | // nfc_llcp_unregister_device
| nfc_llcp_find_local()
nfc_llcp_find_local(...); |
| local_cleanup()
if (!local) { |
rc = -ENODEV; | // nfc_llcp_local_put
goto exit; | kref_put(.., local_release)
} |
| // local_release
| list_del(&local->list)
// nfc_genl_send_params | kfree()
local->dev->idx !!!UAF!!! |
|
and the crash trace for the one of the discussed UAF like:
BUG: KASAN: slab-use-after-free in nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
Read of size 8 at addr ffff888105b0e410 by task 20114
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:319 [inline]
print_report+0xcc/0x620 mm/kasan/report.c:430
kasan_report+0xb2/0xe0 mm/kasan/report.c:536
nfc_genl_send_params net/nfc/netlink.c:999 [inline]
nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
genl_family_rcv_msg_doit.isra.0+0x1ee/0x2e0 net/netlink/genetlink.c:968
genl_family_rcv_msg net/netlink/genetlink.c:1048 [inline]
genl_rcv_msg+0x503/0x7d0 net/netlink/genetlink.c:1065
netlink_rcv_skb+0x161/0x430 net/netlink/af_netlink.c:2548
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1076
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x644/0x900 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x934/0xe70 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg+0x1b6/0x200 net/socket.c:747
____sys_sendmsg+0x6e9/0x890 net/socket.c:2501
___sys_sendmsg+0x110/0x1b0 net/socket.c:2555
__sys_sendmsg+0xf7/0x1d0 net/socket.c:2584
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
RIP: 0033:0x7f34640a2389
RSP: 002b:00007f3463415168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f34641c1f80 RCX: 00007f34640a2389
RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000006
RBP: 00007f34640ed493 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffe38449ecf R14: 00007f3463415300 R15: 0000000000022000
</TASK>
Allocated by task 20116:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
____kasan_kmalloc mm/kasan/common.c:374 [inline]
__kasan_kmalloc+0x7f/0x90 mm/kasan/common.c:383
kmalloc include/linux/slab.h:580 [inline]
kzalloc include/linux/slab.h:720 [inline]
nfc_llcp_register_device+0x49/0xa40 net/nfc/llcp_core.c:1567
nfc_register_device+0x61/0x260 net/nfc/core.c:1124
nci_register_device+0x776/0xb20 net/nfc/nci/core.c:1257
virtual_ncidev_open+0x147/0x230 drivers/nfc/virtual_ncidev.c:148
misc_open+0x379/0x4a0 drivers/char/misc.c:165
chrdev_open+0x26c/0x780 fs/char_dev.c:414
do_dentry_open+0x6c4/0x12a0 fs/open.c:920
do_open fs/namei.c:3560 [inline]
path_openat+0x24fe/0x37e0 fs/namei.c:3715
do_filp_open+0x1ba/0x410 fs/namei.c:3742
do_sys_openat2+0x171/0x4c0 fs/open.c:1356
do_sys_open fs/open.c:1372 [inline]
__do_sys_openat fs/open.c:1388 [inline]
__se_sys_openat fs/open.c:1383 [inline]
__x64_sys_openat+0x143/0x200 fs/open.c:1383
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Freed by task 20115:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521
____kasan_slab_free mm/kasan/common.c:236 [inline]
____kasan_slab_free mm/kasan/common.c:200 [inline]
__kasan_slab_free+0x10a/0x190 mm/kasan/common.c:244
kasan_slab_free include/linux/kasan.h:162 [inline]
slab_free_hook mm/slub.c:1781 [inline]
slab_free_freelist_hook mm/slub.c:1807 [inline]
slab_free mm/slub.c:3787 [inline]
__kmem_cache_free+0x7a/0x190 mm/slub.c:3800
local_release net/nfc/llcp_core.c:174 [inline]
kref_put include/linux/kref.h:65 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:182 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:177 [inline]
nfc_llcp_unregister_device+0x206/0x290 net/nfc/llcp_core.c:1620
nfc_unregister_device+0x160/0x1d0 net/nfc/core.c:1179
virtual_ncidev_close+0x52/0xa0 drivers/nfc/virtual_ncidev.c:163
__fput+0x252/0xa20 fs/file_table.c:321
task_work_run+0x174/0x270 kernel/task_work.c:179
resume_user_mode_work include/linux/resume_user_mode.h:49 [inline]
exit_to_user_mode_loop kernel/entry/common.c:171 [inline]
exit_to_user_mode_prepare+0x108/0x110 kernel/entry/common.c:204
__syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline]
syscall_exit_to_user_mode+0x21/0x50 kernel/entry/common.c:297
do_syscall_64+0x4c/0x90 arch/x86/entry/common.c:86
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Last potentially related work creation:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
__kasan_record_aux_stack+0x95/0xb0 mm/kasan/generic.c:491
kvfree_call_rcu+0x29/0xa80 kernel/rcu/tree.c:3328
drop_sysctl_table+0x3be/0x4e0 fs/proc/proc_sysctl.c:1735
unregister_sysctl_table.part.0+0x9c/0x190 fs/proc/proc_sysctl.c:1773
unregister_sysctl_table+0x24/0x30 fs/proc/proc_sysctl.c:1753
neigh_sysctl_unregister+0x5f/0x80 net/core/neighbour.c:3895
addrconf_notify+0x140/0x17b0 net/ipv6/addrconf.c:3684
notifier_call_chain+0xbe/0x210 kernel/notifier.c:87
call_netdevice_notifiers_info+0xb5/0x150 net/core/dev.c:1937
call_netdevice_notifiers_extack net/core/dev.c:1975 [inline]
call_netdevice_notifiers net/core/dev.c:1989 [inline]
dev_change_name+0x3c3/0x870 net/core/dev.c:1211
dev_ifsioc+0x800/0xf70 net/core/dev_ioctl.c:376
dev_ioctl+0x3d9/0xf80 net/core/dev_ioctl.c:542
sock_do_ioctl+0x160/0x260 net/socket.c:1213
sock_ioctl+0x3f9/0x670 net/socket.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:870 [inline]
__se_sys_ioctl fs/ioctl.c:856 [inline]
__x64_sys_ioctl+0x19e/0x210 fs/ioctl.c:856
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
The buggy address belongs to the object at ffff888105b0e400
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 16 bytes inside of
freed 1024-byte region [ffff888105b0e400, ffff888105b0e800)
The buggy address belongs to the physical page:
head:ffffea000416c200 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000010200(slab|head|node=0|zone=2)
raw: 0200000000010200 ffff8881000430c0 ffffea00044c7010 ffffea0004510e10
raw: 0000000000000000 00000000000a000a 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888105b0e300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888105b0e380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888105b0e400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888105b0e480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888105b0e500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
In summary, this patch solves those use-after-free by
1. Re-implement the nfc_llcp_find_local(). The current version does not
grab the reference when getting the local from the linked list. For
example, the llcp_sock_bind() gets the reference like below:
// llcp_sock_bind()
local = nfc_llcp_find_local(dev); // A
..... \
| raceable
..... /
llcp_sock->local = nfc_llcp_local_get(local); // B
There is an apparent race window that one can drop the reference
and free the local object fetched in (A) before (B) gets the reference.
2. Some callers of the nfc_llcp_find_local() do not grab the reference
at all. For example, the nfc_genl_llc_{{get/set}_params/sdreq} functions.
We add the nfc_llcp_local_put() for them. Moreover, we add the necessary
error handling function to put the reference.
3. Add the nfc_llcp_remove_local() helper. The local object is removed
from the linked list in local_release() when all reference is gone. This
patch removes it when nfc_llcp_unregister_device() is called.
Therefore, every caller of nfc_llcp_find_local() will get a reference
even when the nfc_llcp_unregister_device() is called. This promises no
use-after-free for the local object is ever possible.
Fixes: 52feb444a903 ("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support")
Fixes: c7aa12252f51 ("NFC: Take a reference on the LLCP local pointer when creating a socket")
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-06-25 17:10:07 +08:00
|
|
|
struct nfc_llcp_local *res = NULL;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
net: nfc: Fix use-after-free caused by nfc_llcp_find_local
This commit fixes several use-after-free that caused by function
nfc_llcp_find_local(). For example, one UAF can happen when below buggy
time window occurs.
// nfc_genl_llc_get_params | // nfc_unregister_device
|
dev = nfc_get_device(idx); | device_lock(...)
if (!dev) | dev->shutting_down = true;
return -ENODEV; | device_unlock(...);
|
device_lock(...); | // nfc_llcp_unregister_device
| nfc_llcp_find_local()
nfc_llcp_find_local(...); |
| local_cleanup()
if (!local) { |
rc = -ENODEV; | // nfc_llcp_local_put
goto exit; | kref_put(.., local_release)
} |
| // local_release
| list_del(&local->list)
// nfc_genl_send_params | kfree()
local->dev->idx !!!UAF!!! |
|
and the crash trace for the one of the discussed UAF like:
BUG: KASAN: slab-use-after-free in nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
Read of size 8 at addr ffff888105b0e410 by task 20114
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:319 [inline]
print_report+0xcc/0x620 mm/kasan/report.c:430
kasan_report+0xb2/0xe0 mm/kasan/report.c:536
nfc_genl_send_params net/nfc/netlink.c:999 [inline]
nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
genl_family_rcv_msg_doit.isra.0+0x1ee/0x2e0 net/netlink/genetlink.c:968
genl_family_rcv_msg net/netlink/genetlink.c:1048 [inline]
genl_rcv_msg+0x503/0x7d0 net/netlink/genetlink.c:1065
netlink_rcv_skb+0x161/0x430 net/netlink/af_netlink.c:2548
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1076
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x644/0x900 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x934/0xe70 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg+0x1b6/0x200 net/socket.c:747
____sys_sendmsg+0x6e9/0x890 net/socket.c:2501
___sys_sendmsg+0x110/0x1b0 net/socket.c:2555
__sys_sendmsg+0xf7/0x1d0 net/socket.c:2584
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
RIP: 0033:0x7f34640a2389
RSP: 002b:00007f3463415168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f34641c1f80 RCX: 00007f34640a2389
RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000006
RBP: 00007f34640ed493 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffe38449ecf R14: 00007f3463415300 R15: 0000000000022000
</TASK>
Allocated by task 20116:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
____kasan_kmalloc mm/kasan/common.c:374 [inline]
__kasan_kmalloc+0x7f/0x90 mm/kasan/common.c:383
kmalloc include/linux/slab.h:580 [inline]
kzalloc include/linux/slab.h:720 [inline]
nfc_llcp_register_device+0x49/0xa40 net/nfc/llcp_core.c:1567
nfc_register_device+0x61/0x260 net/nfc/core.c:1124
nci_register_device+0x776/0xb20 net/nfc/nci/core.c:1257
virtual_ncidev_open+0x147/0x230 drivers/nfc/virtual_ncidev.c:148
misc_open+0x379/0x4a0 drivers/char/misc.c:165
chrdev_open+0x26c/0x780 fs/char_dev.c:414
do_dentry_open+0x6c4/0x12a0 fs/open.c:920
do_open fs/namei.c:3560 [inline]
path_openat+0x24fe/0x37e0 fs/namei.c:3715
do_filp_open+0x1ba/0x410 fs/namei.c:3742
do_sys_openat2+0x171/0x4c0 fs/open.c:1356
do_sys_open fs/open.c:1372 [inline]
__do_sys_openat fs/open.c:1388 [inline]
__se_sys_openat fs/open.c:1383 [inline]
__x64_sys_openat+0x143/0x200 fs/open.c:1383
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Freed by task 20115:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521
____kasan_slab_free mm/kasan/common.c:236 [inline]
____kasan_slab_free mm/kasan/common.c:200 [inline]
__kasan_slab_free+0x10a/0x190 mm/kasan/common.c:244
kasan_slab_free include/linux/kasan.h:162 [inline]
slab_free_hook mm/slub.c:1781 [inline]
slab_free_freelist_hook mm/slub.c:1807 [inline]
slab_free mm/slub.c:3787 [inline]
__kmem_cache_free+0x7a/0x190 mm/slub.c:3800
local_release net/nfc/llcp_core.c:174 [inline]
kref_put include/linux/kref.h:65 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:182 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:177 [inline]
nfc_llcp_unregister_device+0x206/0x290 net/nfc/llcp_core.c:1620
nfc_unregister_device+0x160/0x1d0 net/nfc/core.c:1179
virtual_ncidev_close+0x52/0xa0 drivers/nfc/virtual_ncidev.c:163
__fput+0x252/0xa20 fs/file_table.c:321
task_work_run+0x174/0x270 kernel/task_work.c:179
resume_user_mode_work include/linux/resume_user_mode.h:49 [inline]
exit_to_user_mode_loop kernel/entry/common.c:171 [inline]
exit_to_user_mode_prepare+0x108/0x110 kernel/entry/common.c:204
__syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline]
syscall_exit_to_user_mode+0x21/0x50 kernel/entry/common.c:297
do_syscall_64+0x4c/0x90 arch/x86/entry/common.c:86
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Last potentially related work creation:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
__kasan_record_aux_stack+0x95/0xb0 mm/kasan/generic.c:491
kvfree_call_rcu+0x29/0xa80 kernel/rcu/tree.c:3328
drop_sysctl_table+0x3be/0x4e0 fs/proc/proc_sysctl.c:1735
unregister_sysctl_table.part.0+0x9c/0x190 fs/proc/proc_sysctl.c:1773
unregister_sysctl_table+0x24/0x30 fs/proc/proc_sysctl.c:1753
neigh_sysctl_unregister+0x5f/0x80 net/core/neighbour.c:3895
addrconf_notify+0x140/0x17b0 net/ipv6/addrconf.c:3684
notifier_call_chain+0xbe/0x210 kernel/notifier.c:87
call_netdevice_notifiers_info+0xb5/0x150 net/core/dev.c:1937
call_netdevice_notifiers_extack net/core/dev.c:1975 [inline]
call_netdevice_notifiers net/core/dev.c:1989 [inline]
dev_change_name+0x3c3/0x870 net/core/dev.c:1211
dev_ifsioc+0x800/0xf70 net/core/dev_ioctl.c:376
dev_ioctl+0x3d9/0xf80 net/core/dev_ioctl.c:542
sock_do_ioctl+0x160/0x260 net/socket.c:1213
sock_ioctl+0x3f9/0x670 net/socket.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:870 [inline]
__se_sys_ioctl fs/ioctl.c:856 [inline]
__x64_sys_ioctl+0x19e/0x210 fs/ioctl.c:856
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
The buggy address belongs to the object at ffff888105b0e400
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 16 bytes inside of
freed 1024-byte region [ffff888105b0e400, ffff888105b0e800)
The buggy address belongs to the physical page:
head:ffffea000416c200 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000010200(slab|head|node=0|zone=2)
raw: 0200000000010200 ffff8881000430c0 ffffea00044c7010 ffffea0004510e10
raw: 0000000000000000 00000000000a000a 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888105b0e300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888105b0e380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888105b0e400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888105b0e480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888105b0e500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
In summary, this patch solves those use-after-free by
1. Re-implement the nfc_llcp_find_local(). The current version does not
grab the reference when getting the local from the linked list. For
example, the llcp_sock_bind() gets the reference like below:
// llcp_sock_bind()
local = nfc_llcp_find_local(dev); // A
..... \
| raceable
..... /
llcp_sock->local = nfc_llcp_local_get(local); // B
There is an apparent race window that one can drop the reference
and free the local object fetched in (A) before (B) gets the reference.
2. Some callers of the nfc_llcp_find_local() do not grab the reference
at all. For example, the nfc_genl_llc_{{get/set}_params/sdreq} functions.
We add the nfc_llcp_local_put() for them. Moreover, we add the necessary
error handling function to put the reference.
3. Add the nfc_llcp_remove_local() helper. The local object is removed
from the linked list in local_release() when all reference is gone. This
patch removes it when nfc_llcp_unregister_device() is called.
Therefore, every caller of nfc_llcp_find_local() will get a reference
even when the nfc_llcp_unregister_device() is called. This promises no
use-after-free for the local object is ever possible.
Fixes: 52feb444a903 ("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support")
Fixes: c7aa12252f51 ("NFC: Take a reference on the LLCP local pointer when creating a socket")
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-06-25 17:10:07 +08:00
|
|
|
spin_lock(&llcp_devices_lock);
|
2014-02-26 10:26:45 +08:00
|
|
|
list_for_each_entry(local, &llcp_devices, list)
|
net: nfc: Fix use-after-free caused by nfc_llcp_find_local
This commit fixes several use-after-free that caused by function
nfc_llcp_find_local(). For example, one UAF can happen when below buggy
time window occurs.
// nfc_genl_llc_get_params | // nfc_unregister_device
|
dev = nfc_get_device(idx); | device_lock(...)
if (!dev) | dev->shutting_down = true;
return -ENODEV; | device_unlock(...);
|
device_lock(...); | // nfc_llcp_unregister_device
| nfc_llcp_find_local()
nfc_llcp_find_local(...); |
| local_cleanup()
if (!local) { |
rc = -ENODEV; | // nfc_llcp_local_put
goto exit; | kref_put(.., local_release)
} |
| // local_release
| list_del(&local->list)
// nfc_genl_send_params | kfree()
local->dev->idx !!!UAF!!! |
|
and the crash trace for the one of the discussed UAF like:
BUG: KASAN: slab-use-after-free in nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
Read of size 8 at addr ffff888105b0e410 by task 20114
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:319 [inline]
print_report+0xcc/0x620 mm/kasan/report.c:430
kasan_report+0xb2/0xe0 mm/kasan/report.c:536
nfc_genl_send_params net/nfc/netlink.c:999 [inline]
nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
genl_family_rcv_msg_doit.isra.0+0x1ee/0x2e0 net/netlink/genetlink.c:968
genl_family_rcv_msg net/netlink/genetlink.c:1048 [inline]
genl_rcv_msg+0x503/0x7d0 net/netlink/genetlink.c:1065
netlink_rcv_skb+0x161/0x430 net/netlink/af_netlink.c:2548
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1076
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x644/0x900 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x934/0xe70 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg+0x1b6/0x200 net/socket.c:747
____sys_sendmsg+0x6e9/0x890 net/socket.c:2501
___sys_sendmsg+0x110/0x1b0 net/socket.c:2555
__sys_sendmsg+0xf7/0x1d0 net/socket.c:2584
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
RIP: 0033:0x7f34640a2389
RSP: 002b:00007f3463415168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f34641c1f80 RCX: 00007f34640a2389
RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000006
RBP: 00007f34640ed493 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffe38449ecf R14: 00007f3463415300 R15: 0000000000022000
</TASK>
Allocated by task 20116:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
____kasan_kmalloc mm/kasan/common.c:374 [inline]
__kasan_kmalloc+0x7f/0x90 mm/kasan/common.c:383
kmalloc include/linux/slab.h:580 [inline]
kzalloc include/linux/slab.h:720 [inline]
nfc_llcp_register_device+0x49/0xa40 net/nfc/llcp_core.c:1567
nfc_register_device+0x61/0x260 net/nfc/core.c:1124
nci_register_device+0x776/0xb20 net/nfc/nci/core.c:1257
virtual_ncidev_open+0x147/0x230 drivers/nfc/virtual_ncidev.c:148
misc_open+0x379/0x4a0 drivers/char/misc.c:165
chrdev_open+0x26c/0x780 fs/char_dev.c:414
do_dentry_open+0x6c4/0x12a0 fs/open.c:920
do_open fs/namei.c:3560 [inline]
path_openat+0x24fe/0x37e0 fs/namei.c:3715
do_filp_open+0x1ba/0x410 fs/namei.c:3742
do_sys_openat2+0x171/0x4c0 fs/open.c:1356
do_sys_open fs/open.c:1372 [inline]
__do_sys_openat fs/open.c:1388 [inline]
__se_sys_openat fs/open.c:1383 [inline]
__x64_sys_openat+0x143/0x200 fs/open.c:1383
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Freed by task 20115:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521
____kasan_slab_free mm/kasan/common.c:236 [inline]
____kasan_slab_free mm/kasan/common.c:200 [inline]
__kasan_slab_free+0x10a/0x190 mm/kasan/common.c:244
kasan_slab_free include/linux/kasan.h:162 [inline]
slab_free_hook mm/slub.c:1781 [inline]
slab_free_freelist_hook mm/slub.c:1807 [inline]
slab_free mm/slub.c:3787 [inline]
__kmem_cache_free+0x7a/0x190 mm/slub.c:3800
local_release net/nfc/llcp_core.c:174 [inline]
kref_put include/linux/kref.h:65 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:182 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:177 [inline]
nfc_llcp_unregister_device+0x206/0x290 net/nfc/llcp_core.c:1620
nfc_unregister_device+0x160/0x1d0 net/nfc/core.c:1179
virtual_ncidev_close+0x52/0xa0 drivers/nfc/virtual_ncidev.c:163
__fput+0x252/0xa20 fs/file_table.c:321
task_work_run+0x174/0x270 kernel/task_work.c:179
resume_user_mode_work include/linux/resume_user_mode.h:49 [inline]
exit_to_user_mode_loop kernel/entry/common.c:171 [inline]
exit_to_user_mode_prepare+0x108/0x110 kernel/entry/common.c:204
__syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline]
syscall_exit_to_user_mode+0x21/0x50 kernel/entry/common.c:297
do_syscall_64+0x4c/0x90 arch/x86/entry/common.c:86
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Last potentially related work creation:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
__kasan_record_aux_stack+0x95/0xb0 mm/kasan/generic.c:491
kvfree_call_rcu+0x29/0xa80 kernel/rcu/tree.c:3328
drop_sysctl_table+0x3be/0x4e0 fs/proc/proc_sysctl.c:1735
unregister_sysctl_table.part.0+0x9c/0x190 fs/proc/proc_sysctl.c:1773
unregister_sysctl_table+0x24/0x30 fs/proc/proc_sysctl.c:1753
neigh_sysctl_unregister+0x5f/0x80 net/core/neighbour.c:3895
addrconf_notify+0x140/0x17b0 net/ipv6/addrconf.c:3684
notifier_call_chain+0xbe/0x210 kernel/notifier.c:87
call_netdevice_notifiers_info+0xb5/0x150 net/core/dev.c:1937
call_netdevice_notifiers_extack net/core/dev.c:1975 [inline]
call_netdevice_notifiers net/core/dev.c:1989 [inline]
dev_change_name+0x3c3/0x870 net/core/dev.c:1211
dev_ifsioc+0x800/0xf70 net/core/dev_ioctl.c:376
dev_ioctl+0x3d9/0xf80 net/core/dev_ioctl.c:542
sock_do_ioctl+0x160/0x260 net/socket.c:1213
sock_ioctl+0x3f9/0x670 net/socket.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:870 [inline]
__se_sys_ioctl fs/ioctl.c:856 [inline]
__x64_sys_ioctl+0x19e/0x210 fs/ioctl.c:856
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
The buggy address belongs to the object at ffff888105b0e400
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 16 bytes inside of
freed 1024-byte region [ffff888105b0e400, ffff888105b0e800)
The buggy address belongs to the physical page:
head:ffffea000416c200 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000010200(slab|head|node=0|zone=2)
raw: 0200000000010200 ffff8881000430c0 ffffea00044c7010 ffffea0004510e10
raw: 0000000000000000 00000000000a000a 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888105b0e300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888105b0e380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888105b0e400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888105b0e480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888105b0e500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
In summary, this patch solves those use-after-free by
1. Re-implement the nfc_llcp_find_local(). The current version does not
grab the reference when getting the local from the linked list. For
example, the llcp_sock_bind() gets the reference like below:
// llcp_sock_bind()
local = nfc_llcp_find_local(dev); // A
..... \
| raceable
..... /
llcp_sock->local = nfc_llcp_local_get(local); // B
There is an apparent race window that one can drop the reference
and free the local object fetched in (A) before (B) gets the reference.
2. Some callers of the nfc_llcp_find_local() do not grab the reference
at all. For example, the nfc_genl_llc_{{get/set}_params/sdreq} functions.
We add the nfc_llcp_local_put() for them. Moreover, we add the necessary
error handling function to put the reference.
3. Add the nfc_llcp_remove_local() helper. The local object is removed
from the linked list in local_release() when all reference is gone. This
patch removes it when nfc_llcp_unregister_device() is called.
Therefore, every caller of nfc_llcp_find_local() will get a reference
even when the nfc_llcp_unregister_device() is called. This promises no
use-after-free for the local object is ever possible.
Fixes: 52feb444a903 ("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support")
Fixes: c7aa12252f51 ("NFC: Take a reference on the LLCP local pointer when creating a socket")
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-06-25 17:10:07 +08:00
|
|
|
if (local->dev == dev) {
|
|
|
|
res = nfc_llcp_local_get(local);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
spin_unlock(&llcp_devices_lock);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct nfc_llcp_local *nfc_llcp_remove_local(struct nfc_dev *dev)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local, *tmp;
|
|
|
|
|
|
|
|
spin_lock(&llcp_devices_lock);
|
|
|
|
list_for_each_entry_safe(local, tmp, &llcp_devices, list)
|
|
|
|
if (local->dev == dev) {
|
|
|
|
list_del(&local->list);
|
|
|
|
spin_unlock(&llcp_devices_lock);
|
2011-12-14 23:43:12 +08:00
|
|
|
return local;
|
net: nfc: Fix use-after-free caused by nfc_llcp_find_local
This commit fixes several use-after-free that caused by function
nfc_llcp_find_local(). For example, one UAF can happen when below buggy
time window occurs.
// nfc_genl_llc_get_params | // nfc_unregister_device
|
dev = nfc_get_device(idx); | device_lock(...)
if (!dev) | dev->shutting_down = true;
return -ENODEV; | device_unlock(...);
|
device_lock(...); | // nfc_llcp_unregister_device
| nfc_llcp_find_local()
nfc_llcp_find_local(...); |
| local_cleanup()
if (!local) { |
rc = -ENODEV; | // nfc_llcp_local_put
goto exit; | kref_put(.., local_release)
} |
| // local_release
| list_del(&local->list)
// nfc_genl_send_params | kfree()
local->dev->idx !!!UAF!!! |
|
and the crash trace for the one of the discussed UAF like:
BUG: KASAN: slab-use-after-free in nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
Read of size 8 at addr ffff888105b0e410 by task 20114
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:319 [inline]
print_report+0xcc/0x620 mm/kasan/report.c:430
kasan_report+0xb2/0xe0 mm/kasan/report.c:536
nfc_genl_send_params net/nfc/netlink.c:999 [inline]
nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
genl_family_rcv_msg_doit.isra.0+0x1ee/0x2e0 net/netlink/genetlink.c:968
genl_family_rcv_msg net/netlink/genetlink.c:1048 [inline]
genl_rcv_msg+0x503/0x7d0 net/netlink/genetlink.c:1065
netlink_rcv_skb+0x161/0x430 net/netlink/af_netlink.c:2548
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1076
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x644/0x900 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x934/0xe70 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg+0x1b6/0x200 net/socket.c:747
____sys_sendmsg+0x6e9/0x890 net/socket.c:2501
___sys_sendmsg+0x110/0x1b0 net/socket.c:2555
__sys_sendmsg+0xf7/0x1d0 net/socket.c:2584
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
RIP: 0033:0x7f34640a2389
RSP: 002b:00007f3463415168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f34641c1f80 RCX: 00007f34640a2389
RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000006
RBP: 00007f34640ed493 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffe38449ecf R14: 00007f3463415300 R15: 0000000000022000
</TASK>
Allocated by task 20116:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
____kasan_kmalloc mm/kasan/common.c:374 [inline]
__kasan_kmalloc+0x7f/0x90 mm/kasan/common.c:383
kmalloc include/linux/slab.h:580 [inline]
kzalloc include/linux/slab.h:720 [inline]
nfc_llcp_register_device+0x49/0xa40 net/nfc/llcp_core.c:1567
nfc_register_device+0x61/0x260 net/nfc/core.c:1124
nci_register_device+0x776/0xb20 net/nfc/nci/core.c:1257
virtual_ncidev_open+0x147/0x230 drivers/nfc/virtual_ncidev.c:148
misc_open+0x379/0x4a0 drivers/char/misc.c:165
chrdev_open+0x26c/0x780 fs/char_dev.c:414
do_dentry_open+0x6c4/0x12a0 fs/open.c:920
do_open fs/namei.c:3560 [inline]
path_openat+0x24fe/0x37e0 fs/namei.c:3715
do_filp_open+0x1ba/0x410 fs/namei.c:3742
do_sys_openat2+0x171/0x4c0 fs/open.c:1356
do_sys_open fs/open.c:1372 [inline]
__do_sys_openat fs/open.c:1388 [inline]
__se_sys_openat fs/open.c:1383 [inline]
__x64_sys_openat+0x143/0x200 fs/open.c:1383
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Freed by task 20115:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521
____kasan_slab_free mm/kasan/common.c:236 [inline]
____kasan_slab_free mm/kasan/common.c:200 [inline]
__kasan_slab_free+0x10a/0x190 mm/kasan/common.c:244
kasan_slab_free include/linux/kasan.h:162 [inline]
slab_free_hook mm/slub.c:1781 [inline]
slab_free_freelist_hook mm/slub.c:1807 [inline]
slab_free mm/slub.c:3787 [inline]
__kmem_cache_free+0x7a/0x190 mm/slub.c:3800
local_release net/nfc/llcp_core.c:174 [inline]
kref_put include/linux/kref.h:65 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:182 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:177 [inline]
nfc_llcp_unregister_device+0x206/0x290 net/nfc/llcp_core.c:1620
nfc_unregister_device+0x160/0x1d0 net/nfc/core.c:1179
virtual_ncidev_close+0x52/0xa0 drivers/nfc/virtual_ncidev.c:163
__fput+0x252/0xa20 fs/file_table.c:321
task_work_run+0x174/0x270 kernel/task_work.c:179
resume_user_mode_work include/linux/resume_user_mode.h:49 [inline]
exit_to_user_mode_loop kernel/entry/common.c:171 [inline]
exit_to_user_mode_prepare+0x108/0x110 kernel/entry/common.c:204
__syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline]
syscall_exit_to_user_mode+0x21/0x50 kernel/entry/common.c:297
do_syscall_64+0x4c/0x90 arch/x86/entry/common.c:86
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Last potentially related work creation:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
__kasan_record_aux_stack+0x95/0xb0 mm/kasan/generic.c:491
kvfree_call_rcu+0x29/0xa80 kernel/rcu/tree.c:3328
drop_sysctl_table+0x3be/0x4e0 fs/proc/proc_sysctl.c:1735
unregister_sysctl_table.part.0+0x9c/0x190 fs/proc/proc_sysctl.c:1773
unregister_sysctl_table+0x24/0x30 fs/proc/proc_sysctl.c:1753
neigh_sysctl_unregister+0x5f/0x80 net/core/neighbour.c:3895
addrconf_notify+0x140/0x17b0 net/ipv6/addrconf.c:3684
notifier_call_chain+0xbe/0x210 kernel/notifier.c:87
call_netdevice_notifiers_info+0xb5/0x150 net/core/dev.c:1937
call_netdevice_notifiers_extack net/core/dev.c:1975 [inline]
call_netdevice_notifiers net/core/dev.c:1989 [inline]
dev_change_name+0x3c3/0x870 net/core/dev.c:1211
dev_ifsioc+0x800/0xf70 net/core/dev_ioctl.c:376
dev_ioctl+0x3d9/0xf80 net/core/dev_ioctl.c:542
sock_do_ioctl+0x160/0x260 net/socket.c:1213
sock_ioctl+0x3f9/0x670 net/socket.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:870 [inline]
__se_sys_ioctl fs/ioctl.c:856 [inline]
__x64_sys_ioctl+0x19e/0x210 fs/ioctl.c:856
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
The buggy address belongs to the object at ffff888105b0e400
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 16 bytes inside of
freed 1024-byte region [ffff888105b0e400, ffff888105b0e800)
The buggy address belongs to the physical page:
head:ffffea000416c200 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000010200(slab|head|node=0|zone=2)
raw: 0200000000010200 ffff8881000430c0 ffffea00044c7010 ffffea0004510e10
raw: 0000000000000000 00000000000a000a 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888105b0e300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888105b0e380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888105b0e400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888105b0e480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888105b0e500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
In summary, this patch solves those use-after-free by
1. Re-implement the nfc_llcp_find_local(). The current version does not
grab the reference when getting the local from the linked list. For
example, the llcp_sock_bind() gets the reference like below:
// llcp_sock_bind()
local = nfc_llcp_find_local(dev); // A
..... \
| raceable
..... /
llcp_sock->local = nfc_llcp_local_get(local); // B
There is an apparent race window that one can drop the reference
and free the local object fetched in (A) before (B) gets the reference.
2. Some callers of the nfc_llcp_find_local() do not grab the reference
at all. For example, the nfc_genl_llc_{{get/set}_params/sdreq} functions.
We add the nfc_llcp_local_put() for them. Moreover, we add the necessary
error handling function to put the reference.
3. Add the nfc_llcp_remove_local() helper. The local object is removed
from the linked list in local_release() when all reference is gone. This
patch removes it when nfc_llcp_unregister_device() is called.
Therefore, every caller of nfc_llcp_find_local() will get a reference
even when the nfc_llcp_unregister_device() is called. This promises no
use-after-free for the local object is ever possible.
Fixes: 52feb444a903 ("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support")
Fixes: c7aa12252f51 ("NFC: Take a reference on the LLCP local pointer when creating a socket")
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-06-25 17:10:07 +08:00
|
|
|
}
|
|
|
|
spin_unlock(&llcp_devices_lock);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
net: nfc: Fix use-after-free caused by nfc_llcp_find_local
This commit fixes several use-after-free that caused by function
nfc_llcp_find_local(). For example, one UAF can happen when below buggy
time window occurs.
// nfc_genl_llc_get_params | // nfc_unregister_device
|
dev = nfc_get_device(idx); | device_lock(...)
if (!dev) | dev->shutting_down = true;
return -ENODEV; | device_unlock(...);
|
device_lock(...); | // nfc_llcp_unregister_device
| nfc_llcp_find_local()
nfc_llcp_find_local(...); |
| local_cleanup()
if (!local) { |
rc = -ENODEV; | // nfc_llcp_local_put
goto exit; | kref_put(.., local_release)
} |
| // local_release
| list_del(&local->list)
// nfc_genl_send_params | kfree()
local->dev->idx !!!UAF!!! |
|
and the crash trace for the one of the discussed UAF like:
BUG: KASAN: slab-use-after-free in nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
Read of size 8 at addr ffff888105b0e410 by task 20114
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:319 [inline]
print_report+0xcc/0x620 mm/kasan/report.c:430
kasan_report+0xb2/0xe0 mm/kasan/report.c:536
nfc_genl_send_params net/nfc/netlink.c:999 [inline]
nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
genl_family_rcv_msg_doit.isra.0+0x1ee/0x2e0 net/netlink/genetlink.c:968
genl_family_rcv_msg net/netlink/genetlink.c:1048 [inline]
genl_rcv_msg+0x503/0x7d0 net/netlink/genetlink.c:1065
netlink_rcv_skb+0x161/0x430 net/netlink/af_netlink.c:2548
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1076
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x644/0x900 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x934/0xe70 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg+0x1b6/0x200 net/socket.c:747
____sys_sendmsg+0x6e9/0x890 net/socket.c:2501
___sys_sendmsg+0x110/0x1b0 net/socket.c:2555
__sys_sendmsg+0xf7/0x1d0 net/socket.c:2584
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
RIP: 0033:0x7f34640a2389
RSP: 002b:00007f3463415168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f34641c1f80 RCX: 00007f34640a2389
RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000006
RBP: 00007f34640ed493 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffe38449ecf R14: 00007f3463415300 R15: 0000000000022000
</TASK>
Allocated by task 20116:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
____kasan_kmalloc mm/kasan/common.c:374 [inline]
__kasan_kmalloc+0x7f/0x90 mm/kasan/common.c:383
kmalloc include/linux/slab.h:580 [inline]
kzalloc include/linux/slab.h:720 [inline]
nfc_llcp_register_device+0x49/0xa40 net/nfc/llcp_core.c:1567
nfc_register_device+0x61/0x260 net/nfc/core.c:1124
nci_register_device+0x776/0xb20 net/nfc/nci/core.c:1257
virtual_ncidev_open+0x147/0x230 drivers/nfc/virtual_ncidev.c:148
misc_open+0x379/0x4a0 drivers/char/misc.c:165
chrdev_open+0x26c/0x780 fs/char_dev.c:414
do_dentry_open+0x6c4/0x12a0 fs/open.c:920
do_open fs/namei.c:3560 [inline]
path_openat+0x24fe/0x37e0 fs/namei.c:3715
do_filp_open+0x1ba/0x410 fs/namei.c:3742
do_sys_openat2+0x171/0x4c0 fs/open.c:1356
do_sys_open fs/open.c:1372 [inline]
__do_sys_openat fs/open.c:1388 [inline]
__se_sys_openat fs/open.c:1383 [inline]
__x64_sys_openat+0x143/0x200 fs/open.c:1383
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Freed by task 20115:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521
____kasan_slab_free mm/kasan/common.c:236 [inline]
____kasan_slab_free mm/kasan/common.c:200 [inline]
__kasan_slab_free+0x10a/0x190 mm/kasan/common.c:244
kasan_slab_free include/linux/kasan.h:162 [inline]
slab_free_hook mm/slub.c:1781 [inline]
slab_free_freelist_hook mm/slub.c:1807 [inline]
slab_free mm/slub.c:3787 [inline]
__kmem_cache_free+0x7a/0x190 mm/slub.c:3800
local_release net/nfc/llcp_core.c:174 [inline]
kref_put include/linux/kref.h:65 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:182 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:177 [inline]
nfc_llcp_unregister_device+0x206/0x290 net/nfc/llcp_core.c:1620
nfc_unregister_device+0x160/0x1d0 net/nfc/core.c:1179
virtual_ncidev_close+0x52/0xa0 drivers/nfc/virtual_ncidev.c:163
__fput+0x252/0xa20 fs/file_table.c:321
task_work_run+0x174/0x270 kernel/task_work.c:179
resume_user_mode_work include/linux/resume_user_mode.h:49 [inline]
exit_to_user_mode_loop kernel/entry/common.c:171 [inline]
exit_to_user_mode_prepare+0x108/0x110 kernel/entry/common.c:204
__syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline]
syscall_exit_to_user_mode+0x21/0x50 kernel/entry/common.c:297
do_syscall_64+0x4c/0x90 arch/x86/entry/common.c:86
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Last potentially related work creation:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
__kasan_record_aux_stack+0x95/0xb0 mm/kasan/generic.c:491
kvfree_call_rcu+0x29/0xa80 kernel/rcu/tree.c:3328
drop_sysctl_table+0x3be/0x4e0 fs/proc/proc_sysctl.c:1735
unregister_sysctl_table.part.0+0x9c/0x190 fs/proc/proc_sysctl.c:1773
unregister_sysctl_table+0x24/0x30 fs/proc/proc_sysctl.c:1753
neigh_sysctl_unregister+0x5f/0x80 net/core/neighbour.c:3895
addrconf_notify+0x140/0x17b0 net/ipv6/addrconf.c:3684
notifier_call_chain+0xbe/0x210 kernel/notifier.c:87
call_netdevice_notifiers_info+0xb5/0x150 net/core/dev.c:1937
call_netdevice_notifiers_extack net/core/dev.c:1975 [inline]
call_netdevice_notifiers net/core/dev.c:1989 [inline]
dev_change_name+0x3c3/0x870 net/core/dev.c:1211
dev_ifsioc+0x800/0xf70 net/core/dev_ioctl.c:376
dev_ioctl+0x3d9/0xf80 net/core/dev_ioctl.c:542
sock_do_ioctl+0x160/0x260 net/socket.c:1213
sock_ioctl+0x3f9/0x670 net/socket.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:870 [inline]
__se_sys_ioctl fs/ioctl.c:856 [inline]
__x64_sys_ioctl+0x19e/0x210 fs/ioctl.c:856
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
The buggy address belongs to the object at ffff888105b0e400
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 16 bytes inside of
freed 1024-byte region [ffff888105b0e400, ffff888105b0e800)
The buggy address belongs to the physical page:
head:ffffea000416c200 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000010200(slab|head|node=0|zone=2)
raw: 0200000000010200 ffff8881000430c0 ffffea00044c7010 ffffea0004510e10
raw: 0000000000000000 00000000000a000a 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888105b0e300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888105b0e380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888105b0e400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888105b0e480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888105b0e500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
In summary, this patch solves those use-after-free by
1. Re-implement the nfc_llcp_find_local(). The current version does not
grab the reference when getting the local from the linked list. For
example, the llcp_sock_bind() gets the reference like below:
// llcp_sock_bind()
local = nfc_llcp_find_local(dev); // A
..... \
| raceable
..... /
llcp_sock->local = nfc_llcp_local_get(local); // B
There is an apparent race window that one can drop the reference
and free the local object fetched in (A) before (B) gets the reference.
2. Some callers of the nfc_llcp_find_local() do not grab the reference
at all. For example, the nfc_genl_llc_{{get/set}_params/sdreq} functions.
We add the nfc_llcp_local_put() for them. Moreover, we add the necessary
error handling function to put the reference.
3. Add the nfc_llcp_remove_local() helper. The local object is removed
from the linked list in local_release() when all reference is gone. This
patch removes it when nfc_llcp_unregister_device() is called.
Therefore, every caller of nfc_llcp_find_local() will get a reference
even when the nfc_llcp_unregister_device() is called. This promises no
use-after-free for the local object is ever possible.
Fixes: 52feb444a903 ("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support")
Fixes: c7aa12252f51 ("NFC: Take a reference on the LLCP local pointer when creating a socket")
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-06-25 17:10:07 +08:00
|
|
|
pr_warn("Shutting down device not found\n");
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *wks[] = {
|
|
|
|
NULL,
|
|
|
|
NULL, /* SDP */
|
|
|
|
"urn:nfc:sn:ip",
|
|
|
|
"urn:nfc:sn:obex",
|
|
|
|
"urn:nfc:sn:snep",
|
|
|
|
};
|
|
|
|
|
2021-07-30 22:41:59 +08:00
|
|
|
static int nfc_llcp_wks_sap(const char *service_name, size_t service_name_len)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
|
|
|
int sap, num_wks;
|
|
|
|
|
|
|
|
pr_debug("%s\n", service_name);
|
|
|
|
|
|
|
|
if (service_name == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
num_wks = ARRAY_SIZE(wks);
|
|
|
|
|
2012-03-05 08:03:52 +08:00
|
|
|
for (sap = 0; sap < num_wks; sap++) {
|
2011-12-14 23:43:12 +08:00
|
|
|
if (wks[sap] == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (strncmp(wks[sap], service_name, service_name_len) == 0)
|
|
|
|
return sap;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2012-06-25 21:46:28 +08:00
|
|
|
static
|
|
|
|
struct nfc_llcp_sock *nfc_llcp_sock_from_sn(struct nfc_llcp_local *local,
|
2023-10-09 20:31:10 +08:00
|
|
|
const u8 *sn, size_t sn_len,
|
|
|
|
bool needref)
|
2012-06-25 21:46:28 +08:00
|
|
|
{
|
|
|
|
struct sock *sk;
|
|
|
|
struct nfc_llcp_sock *llcp_sock, *tmp_sock;
|
|
|
|
|
|
|
|
pr_debug("sn %zd %p\n", sn_len, sn);
|
|
|
|
|
|
|
|
if (sn == NULL || sn_len == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
read_lock(&local->sockets.lock);
|
|
|
|
|
|
|
|
llcp_sock = NULL;
|
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
sk_for_each(sk, &local->sockets.head) {
|
2012-06-25 21:46:28 +08:00
|
|
|
tmp_sock = nfc_llcp_sock(sk);
|
|
|
|
|
|
|
|
pr_debug("llcp sock %p\n", tmp_sock);
|
|
|
|
|
2012-10-15 21:08:29 +08:00
|
|
|
if (tmp_sock->sk.sk_type == SOCK_STREAM &&
|
|
|
|
tmp_sock->sk.sk_state != LLCP_LISTEN)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (tmp_sock->sk.sk_type == SOCK_DGRAM &&
|
|
|
|
tmp_sock->sk.sk_state != LLCP_BOUND)
|
2012-06-25 21:46:28 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (tmp_sock->service_name == NULL ||
|
|
|
|
tmp_sock->service_name_len == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (tmp_sock->service_name_len != sn_len)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (memcmp(sn, tmp_sock->service_name, sn_len) == 0) {
|
|
|
|
llcp_sock = tmp_sock;
|
2023-10-09 20:31:10 +08:00
|
|
|
if (needref)
|
|
|
|
sock_hold(&llcp_sock->sk);
|
2012-06-25 21:46:28 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
read_unlock(&local->sockets.lock);
|
|
|
|
|
|
|
|
pr_debug("Found llcp sock %p\n", llcp_sock);
|
|
|
|
|
|
|
|
return llcp_sock;
|
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local,
|
2012-03-05 08:03:52 +08:00
|
|
|
struct nfc_llcp_sock *sock)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
|
|
|
mutex_lock(&local->sdp_lock);
|
|
|
|
|
|
|
|
if (sock->service_name != NULL && sock->service_name_len > 0) {
|
|
|
|
int ssap = nfc_llcp_wks_sap(sock->service_name,
|
2012-03-05 08:03:52 +08:00
|
|
|
sock->service_name_len);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
if (ssap > 0) {
|
|
|
|
pr_debug("WKS %d\n", ssap);
|
|
|
|
|
|
|
|
/* This is a WKS, let's check if it's free */
|
2022-03-03 03:25:21 +08:00
|
|
|
if (test_bit(ssap, &local->local_wks)) {
|
2011-12-14 23:43:12 +08:00
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
|
|
|
return LLCP_SAP_MAX;
|
|
|
|
}
|
|
|
|
|
2012-03-05 08:03:38 +08:00
|
|
|
set_bit(ssap, &local->local_wks);
|
2011-12-14 23:43:12 +08:00
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
|
|
|
return ssap;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-06-25 21:46:28 +08:00
|
|
|
* Check if there already is a non WKS socket bound
|
|
|
|
* to this service name.
|
2011-12-14 23:43:12 +08:00
|
|
|
*/
|
2012-06-25 21:46:28 +08:00
|
|
|
if (nfc_llcp_sock_from_sn(local, sock->service_name,
|
2023-10-09 20:31:10 +08:00
|
|
|
sock->service_name_len,
|
|
|
|
false) != NULL) {
|
2011-12-14 23:43:12 +08:00
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
|
|
|
return LLCP_SAP_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
2012-06-25 21:46:28 +08:00
|
|
|
return LLCP_SDP_UNBOUND;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-06-22 23:15:20 +08:00
|
|
|
} else if (sock->ssap != 0 && sock->ssap < LLCP_WKS_NUM_SAP) {
|
|
|
|
if (!test_bit(sock->ssap, &local->local_wks)) {
|
|
|
|
set_bit(sock->ssap, &local->local_wks);
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-06-22 23:15:20 +08:00
|
|
|
return sock->ssap;
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
|
|
|
return LLCP_SAP_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 nfc_llcp_get_local_ssap(struct nfc_llcp_local *local)
|
|
|
|
{
|
|
|
|
u8 local_ssap;
|
|
|
|
|
|
|
|
mutex_lock(&local->sdp_lock);
|
|
|
|
|
|
|
|
local_ssap = find_first_zero_bit(&local->local_sap, LLCP_LOCAL_NUM_SAP);
|
|
|
|
if (local_ssap == LLCP_LOCAL_NUM_SAP) {
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
return LLCP_SAP_MAX;
|
|
|
|
}
|
|
|
|
|
2012-03-05 08:03:38 +08:00
|
|
|
set_bit(local_ssap, &local->local_sap);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
|
|
|
return local_ssap + LLCP_LOCAL_SAP_OFFSET;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfc_llcp_put_ssap(struct nfc_llcp_local *local, u8 ssap)
|
|
|
|
{
|
|
|
|
u8 local_ssap;
|
|
|
|
unsigned long *sdp;
|
|
|
|
|
|
|
|
if (ssap < LLCP_WKS_NUM_SAP) {
|
|
|
|
local_ssap = ssap;
|
|
|
|
sdp = &local->local_wks;
|
|
|
|
} else if (ssap < LLCP_LOCAL_NUM_SAP) {
|
2012-06-25 21:46:28 +08:00
|
|
|
atomic_t *client_cnt;
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
local_ssap = ssap - LLCP_WKS_NUM_SAP;
|
|
|
|
sdp = &local->local_sdp;
|
2012-06-25 21:46:28 +08:00
|
|
|
client_cnt = &local->local_sdp_cnt[local_ssap];
|
|
|
|
|
|
|
|
pr_debug("%d clients\n", atomic_read(client_cnt));
|
|
|
|
|
|
|
|
mutex_lock(&local->sdp_lock);
|
|
|
|
|
|
|
|
if (atomic_dec_and_test(client_cnt)) {
|
|
|
|
struct nfc_llcp_sock *l_sock;
|
|
|
|
|
|
|
|
pr_debug("No more clients for SAP %d\n", ssap);
|
|
|
|
|
|
|
|
clear_bit(local_ssap, sdp);
|
|
|
|
|
|
|
|
/* Find the listening sock and set it back to UNBOUND */
|
|
|
|
l_sock = nfc_llcp_sock_get(local, ssap, LLCP_SAP_SDP);
|
|
|
|
if (l_sock) {
|
|
|
|
l_sock->ssap = LLCP_SDP_UNBOUND;
|
|
|
|
nfc_llcp_sock_put(l_sock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
|
|
|
return;
|
2011-12-14 23:43:12 +08:00
|
|
|
} else if (ssap < LLCP_MAX_SAP) {
|
|
|
|
local_ssap = ssap - LLCP_LOCAL_NUM_SAP;
|
|
|
|
sdp = &local->local_sap;
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_lock(&local->sdp_lock);
|
|
|
|
|
2012-03-05 08:03:38 +08:00
|
|
|
clear_bit(local_ssap, sdp);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
}
|
|
|
|
|
2012-06-25 21:46:28 +08:00
|
|
|
static u8 nfc_llcp_reserve_sdp_ssap(struct nfc_llcp_local *local)
|
|
|
|
{
|
|
|
|
u8 ssap;
|
|
|
|
|
|
|
|
mutex_lock(&local->sdp_lock);
|
|
|
|
|
|
|
|
ssap = find_first_zero_bit(&local->local_sdp, LLCP_SDP_NUM_SAP);
|
|
|
|
if (ssap == LLCP_SDP_NUM_SAP) {
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
|
|
|
return LLCP_SAP_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_debug("SDP ssap %d\n", LLCP_WKS_NUM_SAP + ssap);
|
|
|
|
|
|
|
|
set_bit(ssap, &local->local_sdp);
|
|
|
|
|
|
|
|
mutex_unlock(&local->sdp_lock);
|
|
|
|
|
|
|
|
return LLCP_WKS_NUM_SAP + ssap;
|
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
|
|
|
|
{
|
2019-02-22 15:37:58 +08:00
|
|
|
u8 *gb_cur, version, version_length;
|
|
|
|
u8 lto_length, wks_length, miux_length;
|
2021-07-30 22:41:59 +08:00
|
|
|
const u8 *version_tlv = NULL, *lto_tlv = NULL,
|
2019-02-22 15:37:58 +08:00
|
|
|
*wks_tlv = NULL, *miux_tlv = NULL;
|
2013-06-03 18:10:04 +08:00
|
|
|
__be16 wks = cpu_to_be16(local->local_wks);
|
2011-12-14 23:43:12 +08:00
|
|
|
u8 gb_len = 0;
|
2012-09-02 21:21:46 +08:00
|
|
|
int ret = 0;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
version = LLCP_VERSION_11;
|
|
|
|
version_tlv = nfc_llcp_build_tlv(LLCP_TLV_VERSION, &version,
|
2012-03-05 08:03:52 +08:00
|
|
|
1, &version_length);
|
2019-02-22 15:37:58 +08:00
|
|
|
if (!version_tlv) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2011-12-14 23:43:12 +08:00
|
|
|
gb_len += version_length;
|
|
|
|
|
2012-10-17 20:43:39 +08:00
|
|
|
lto_tlv = nfc_llcp_build_tlv(LLCP_TLV_LTO, &local->lto, 1, <o_length);
|
2019-02-22 15:37:58 +08:00
|
|
|
if (!lto_tlv) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2011-12-14 23:43:12 +08:00
|
|
|
gb_len += lto_length;
|
|
|
|
|
|
|
|
pr_debug("Local wks 0x%lx\n", local->local_wks);
|
2013-06-03 18:10:04 +08:00
|
|
|
wks_tlv = nfc_llcp_build_tlv(LLCP_TLV_WKS, (u8 *)&wks, 2, &wks_length);
|
2019-02-22 15:37:58 +08:00
|
|
|
if (!wks_tlv) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2011-12-14 23:43:12 +08:00
|
|
|
gb_len += wks_length;
|
|
|
|
|
2012-10-17 20:43:39 +08:00
|
|
|
miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0,
|
2012-04-11 01:43:19 +08:00
|
|
|
&miux_length);
|
2019-02-22 15:37:58 +08:00
|
|
|
if (!miux_tlv) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-04-11 01:43:19 +08:00
|
|
|
gb_len += miux_length;
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
gb_len += ARRAY_SIZE(llcp_magic);
|
|
|
|
|
|
|
|
if (gb_len > NFC_MAX_GT_LEN) {
|
2012-09-02 21:21:46 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
gb_cur = local->gb;
|
|
|
|
|
|
|
|
memcpy(gb_cur, llcp_magic, ARRAY_SIZE(llcp_magic));
|
|
|
|
gb_cur += ARRAY_SIZE(llcp_magic);
|
|
|
|
|
|
|
|
memcpy(gb_cur, version_tlv, version_length);
|
|
|
|
gb_cur += version_length;
|
|
|
|
|
|
|
|
memcpy(gb_cur, lto_tlv, lto_length);
|
|
|
|
gb_cur += lto_length;
|
|
|
|
|
|
|
|
memcpy(gb_cur, wks_tlv, wks_length);
|
|
|
|
gb_cur += wks_length;
|
|
|
|
|
2012-04-11 01:43:19 +08:00
|
|
|
memcpy(gb_cur, miux_tlv, miux_length);
|
|
|
|
gb_cur += miux_length;
|
|
|
|
|
2012-09-02 21:21:46 +08:00
|
|
|
local->gb_len = gb_len;
|
|
|
|
|
|
|
|
out:
|
2011-12-14 23:43:12 +08:00
|
|
|
kfree(version_tlv);
|
|
|
|
kfree(lto_tlv);
|
2012-09-02 21:21:46 +08:00
|
|
|
kfree(wks_tlv);
|
|
|
|
kfree(miux_tlv);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-09-02 21:21:46 +08:00
|
|
|
return ret;
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
2012-06-22 08:04:53 +08:00
|
|
|
u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local;
|
|
|
|
|
|
|
|
local = nfc_llcp_find_local(dev);
|
|
|
|
if (local == NULL) {
|
|
|
|
*general_bytes_len = 0;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
nfc_llcp_build_gb(local);
|
|
|
|
|
|
|
|
*general_bytes_len = local->gb_len;
|
|
|
|
|
net: nfc: Fix use-after-free caused by nfc_llcp_find_local
This commit fixes several use-after-free that caused by function
nfc_llcp_find_local(). For example, one UAF can happen when below buggy
time window occurs.
// nfc_genl_llc_get_params | // nfc_unregister_device
|
dev = nfc_get_device(idx); | device_lock(...)
if (!dev) | dev->shutting_down = true;
return -ENODEV; | device_unlock(...);
|
device_lock(...); | // nfc_llcp_unregister_device
| nfc_llcp_find_local()
nfc_llcp_find_local(...); |
| local_cleanup()
if (!local) { |
rc = -ENODEV; | // nfc_llcp_local_put
goto exit; | kref_put(.., local_release)
} |
| // local_release
| list_del(&local->list)
// nfc_genl_send_params | kfree()
local->dev->idx !!!UAF!!! |
|
and the crash trace for the one of the discussed UAF like:
BUG: KASAN: slab-use-after-free in nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
Read of size 8 at addr ffff888105b0e410 by task 20114
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:319 [inline]
print_report+0xcc/0x620 mm/kasan/report.c:430
kasan_report+0xb2/0xe0 mm/kasan/report.c:536
nfc_genl_send_params net/nfc/netlink.c:999 [inline]
nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
genl_family_rcv_msg_doit.isra.0+0x1ee/0x2e0 net/netlink/genetlink.c:968
genl_family_rcv_msg net/netlink/genetlink.c:1048 [inline]
genl_rcv_msg+0x503/0x7d0 net/netlink/genetlink.c:1065
netlink_rcv_skb+0x161/0x430 net/netlink/af_netlink.c:2548
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1076
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x644/0x900 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x934/0xe70 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg+0x1b6/0x200 net/socket.c:747
____sys_sendmsg+0x6e9/0x890 net/socket.c:2501
___sys_sendmsg+0x110/0x1b0 net/socket.c:2555
__sys_sendmsg+0xf7/0x1d0 net/socket.c:2584
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
RIP: 0033:0x7f34640a2389
RSP: 002b:00007f3463415168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f34641c1f80 RCX: 00007f34640a2389
RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000006
RBP: 00007f34640ed493 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffe38449ecf R14: 00007f3463415300 R15: 0000000000022000
</TASK>
Allocated by task 20116:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
____kasan_kmalloc mm/kasan/common.c:374 [inline]
__kasan_kmalloc+0x7f/0x90 mm/kasan/common.c:383
kmalloc include/linux/slab.h:580 [inline]
kzalloc include/linux/slab.h:720 [inline]
nfc_llcp_register_device+0x49/0xa40 net/nfc/llcp_core.c:1567
nfc_register_device+0x61/0x260 net/nfc/core.c:1124
nci_register_device+0x776/0xb20 net/nfc/nci/core.c:1257
virtual_ncidev_open+0x147/0x230 drivers/nfc/virtual_ncidev.c:148
misc_open+0x379/0x4a0 drivers/char/misc.c:165
chrdev_open+0x26c/0x780 fs/char_dev.c:414
do_dentry_open+0x6c4/0x12a0 fs/open.c:920
do_open fs/namei.c:3560 [inline]
path_openat+0x24fe/0x37e0 fs/namei.c:3715
do_filp_open+0x1ba/0x410 fs/namei.c:3742
do_sys_openat2+0x171/0x4c0 fs/open.c:1356
do_sys_open fs/open.c:1372 [inline]
__do_sys_openat fs/open.c:1388 [inline]
__se_sys_openat fs/open.c:1383 [inline]
__x64_sys_openat+0x143/0x200 fs/open.c:1383
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Freed by task 20115:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521
____kasan_slab_free mm/kasan/common.c:236 [inline]
____kasan_slab_free mm/kasan/common.c:200 [inline]
__kasan_slab_free+0x10a/0x190 mm/kasan/common.c:244
kasan_slab_free include/linux/kasan.h:162 [inline]
slab_free_hook mm/slub.c:1781 [inline]
slab_free_freelist_hook mm/slub.c:1807 [inline]
slab_free mm/slub.c:3787 [inline]
__kmem_cache_free+0x7a/0x190 mm/slub.c:3800
local_release net/nfc/llcp_core.c:174 [inline]
kref_put include/linux/kref.h:65 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:182 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:177 [inline]
nfc_llcp_unregister_device+0x206/0x290 net/nfc/llcp_core.c:1620
nfc_unregister_device+0x160/0x1d0 net/nfc/core.c:1179
virtual_ncidev_close+0x52/0xa0 drivers/nfc/virtual_ncidev.c:163
__fput+0x252/0xa20 fs/file_table.c:321
task_work_run+0x174/0x270 kernel/task_work.c:179
resume_user_mode_work include/linux/resume_user_mode.h:49 [inline]
exit_to_user_mode_loop kernel/entry/common.c:171 [inline]
exit_to_user_mode_prepare+0x108/0x110 kernel/entry/common.c:204
__syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline]
syscall_exit_to_user_mode+0x21/0x50 kernel/entry/common.c:297
do_syscall_64+0x4c/0x90 arch/x86/entry/common.c:86
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Last potentially related work creation:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
__kasan_record_aux_stack+0x95/0xb0 mm/kasan/generic.c:491
kvfree_call_rcu+0x29/0xa80 kernel/rcu/tree.c:3328
drop_sysctl_table+0x3be/0x4e0 fs/proc/proc_sysctl.c:1735
unregister_sysctl_table.part.0+0x9c/0x190 fs/proc/proc_sysctl.c:1773
unregister_sysctl_table+0x24/0x30 fs/proc/proc_sysctl.c:1753
neigh_sysctl_unregister+0x5f/0x80 net/core/neighbour.c:3895
addrconf_notify+0x140/0x17b0 net/ipv6/addrconf.c:3684
notifier_call_chain+0xbe/0x210 kernel/notifier.c:87
call_netdevice_notifiers_info+0xb5/0x150 net/core/dev.c:1937
call_netdevice_notifiers_extack net/core/dev.c:1975 [inline]
call_netdevice_notifiers net/core/dev.c:1989 [inline]
dev_change_name+0x3c3/0x870 net/core/dev.c:1211
dev_ifsioc+0x800/0xf70 net/core/dev_ioctl.c:376
dev_ioctl+0x3d9/0xf80 net/core/dev_ioctl.c:542
sock_do_ioctl+0x160/0x260 net/socket.c:1213
sock_ioctl+0x3f9/0x670 net/socket.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:870 [inline]
__se_sys_ioctl fs/ioctl.c:856 [inline]
__x64_sys_ioctl+0x19e/0x210 fs/ioctl.c:856
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
The buggy address belongs to the object at ffff888105b0e400
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 16 bytes inside of
freed 1024-byte region [ffff888105b0e400, ffff888105b0e800)
The buggy address belongs to the physical page:
head:ffffea000416c200 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000010200(slab|head|node=0|zone=2)
raw: 0200000000010200 ffff8881000430c0 ffffea00044c7010 ffffea0004510e10
raw: 0000000000000000 00000000000a000a 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888105b0e300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888105b0e380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888105b0e400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888105b0e480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888105b0e500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
In summary, this patch solves those use-after-free by
1. Re-implement the nfc_llcp_find_local(). The current version does not
grab the reference when getting the local from the linked list. For
example, the llcp_sock_bind() gets the reference like below:
// llcp_sock_bind()
local = nfc_llcp_find_local(dev); // A
..... \
| raceable
..... /
llcp_sock->local = nfc_llcp_local_get(local); // B
There is an apparent race window that one can drop the reference
and free the local object fetched in (A) before (B) gets the reference.
2. Some callers of the nfc_llcp_find_local() do not grab the reference
at all. For example, the nfc_genl_llc_{{get/set}_params/sdreq} functions.
We add the nfc_llcp_local_put() for them. Moreover, we add the necessary
error handling function to put the reference.
3. Add the nfc_llcp_remove_local() helper. The local object is removed
from the linked list in local_release() when all reference is gone. This
patch removes it when nfc_llcp_unregister_device() is called.
Therefore, every caller of nfc_llcp_find_local() will get a reference
even when the nfc_llcp_unregister_device() is called. This promises no
use-after-free for the local object is ever possible.
Fixes: 52feb444a903 ("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support")
Fixes: c7aa12252f51 ("NFC: Take a reference on the LLCP local pointer when creating a socket")
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-06-25 17:10:07 +08:00
|
|
|
nfc_llcp_local_put(local);
|
|
|
|
|
2012-06-22 08:04:53 +08:00
|
|
|
return local->gb;
|
|
|
|
}
|
|
|
|
|
2021-07-30 22:41:59 +08:00
|
|
|
int nfc_llcp_set_remote_gb(struct nfc_dev *dev, const u8 *gb, u8 gb_len)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
2014-02-25 09:18:10 +08:00
|
|
|
struct nfc_llcp_local *local;
|
net: nfc: Fix use-after-free caused by nfc_llcp_find_local
This commit fixes several use-after-free that caused by function
nfc_llcp_find_local(). For example, one UAF can happen when below buggy
time window occurs.
// nfc_genl_llc_get_params | // nfc_unregister_device
|
dev = nfc_get_device(idx); | device_lock(...)
if (!dev) | dev->shutting_down = true;
return -ENODEV; | device_unlock(...);
|
device_lock(...); | // nfc_llcp_unregister_device
| nfc_llcp_find_local()
nfc_llcp_find_local(...); |
| local_cleanup()
if (!local) { |
rc = -ENODEV; | // nfc_llcp_local_put
goto exit; | kref_put(.., local_release)
} |
| // local_release
| list_del(&local->list)
// nfc_genl_send_params | kfree()
local->dev->idx !!!UAF!!! |
|
and the crash trace for the one of the discussed UAF like:
BUG: KASAN: slab-use-after-free in nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
Read of size 8 at addr ffff888105b0e410 by task 20114
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:319 [inline]
print_report+0xcc/0x620 mm/kasan/report.c:430
kasan_report+0xb2/0xe0 mm/kasan/report.c:536
nfc_genl_send_params net/nfc/netlink.c:999 [inline]
nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
genl_family_rcv_msg_doit.isra.0+0x1ee/0x2e0 net/netlink/genetlink.c:968
genl_family_rcv_msg net/netlink/genetlink.c:1048 [inline]
genl_rcv_msg+0x503/0x7d0 net/netlink/genetlink.c:1065
netlink_rcv_skb+0x161/0x430 net/netlink/af_netlink.c:2548
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1076
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x644/0x900 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x934/0xe70 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg+0x1b6/0x200 net/socket.c:747
____sys_sendmsg+0x6e9/0x890 net/socket.c:2501
___sys_sendmsg+0x110/0x1b0 net/socket.c:2555
__sys_sendmsg+0xf7/0x1d0 net/socket.c:2584
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
RIP: 0033:0x7f34640a2389
RSP: 002b:00007f3463415168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f34641c1f80 RCX: 00007f34640a2389
RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000006
RBP: 00007f34640ed493 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffe38449ecf R14: 00007f3463415300 R15: 0000000000022000
</TASK>
Allocated by task 20116:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
____kasan_kmalloc mm/kasan/common.c:374 [inline]
__kasan_kmalloc+0x7f/0x90 mm/kasan/common.c:383
kmalloc include/linux/slab.h:580 [inline]
kzalloc include/linux/slab.h:720 [inline]
nfc_llcp_register_device+0x49/0xa40 net/nfc/llcp_core.c:1567
nfc_register_device+0x61/0x260 net/nfc/core.c:1124
nci_register_device+0x776/0xb20 net/nfc/nci/core.c:1257
virtual_ncidev_open+0x147/0x230 drivers/nfc/virtual_ncidev.c:148
misc_open+0x379/0x4a0 drivers/char/misc.c:165
chrdev_open+0x26c/0x780 fs/char_dev.c:414
do_dentry_open+0x6c4/0x12a0 fs/open.c:920
do_open fs/namei.c:3560 [inline]
path_openat+0x24fe/0x37e0 fs/namei.c:3715
do_filp_open+0x1ba/0x410 fs/namei.c:3742
do_sys_openat2+0x171/0x4c0 fs/open.c:1356
do_sys_open fs/open.c:1372 [inline]
__do_sys_openat fs/open.c:1388 [inline]
__se_sys_openat fs/open.c:1383 [inline]
__x64_sys_openat+0x143/0x200 fs/open.c:1383
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Freed by task 20115:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521
____kasan_slab_free mm/kasan/common.c:236 [inline]
____kasan_slab_free mm/kasan/common.c:200 [inline]
__kasan_slab_free+0x10a/0x190 mm/kasan/common.c:244
kasan_slab_free include/linux/kasan.h:162 [inline]
slab_free_hook mm/slub.c:1781 [inline]
slab_free_freelist_hook mm/slub.c:1807 [inline]
slab_free mm/slub.c:3787 [inline]
__kmem_cache_free+0x7a/0x190 mm/slub.c:3800
local_release net/nfc/llcp_core.c:174 [inline]
kref_put include/linux/kref.h:65 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:182 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:177 [inline]
nfc_llcp_unregister_device+0x206/0x290 net/nfc/llcp_core.c:1620
nfc_unregister_device+0x160/0x1d0 net/nfc/core.c:1179
virtual_ncidev_close+0x52/0xa0 drivers/nfc/virtual_ncidev.c:163
__fput+0x252/0xa20 fs/file_table.c:321
task_work_run+0x174/0x270 kernel/task_work.c:179
resume_user_mode_work include/linux/resume_user_mode.h:49 [inline]
exit_to_user_mode_loop kernel/entry/common.c:171 [inline]
exit_to_user_mode_prepare+0x108/0x110 kernel/entry/common.c:204
__syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline]
syscall_exit_to_user_mode+0x21/0x50 kernel/entry/common.c:297
do_syscall_64+0x4c/0x90 arch/x86/entry/common.c:86
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Last potentially related work creation:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
__kasan_record_aux_stack+0x95/0xb0 mm/kasan/generic.c:491
kvfree_call_rcu+0x29/0xa80 kernel/rcu/tree.c:3328
drop_sysctl_table+0x3be/0x4e0 fs/proc/proc_sysctl.c:1735
unregister_sysctl_table.part.0+0x9c/0x190 fs/proc/proc_sysctl.c:1773
unregister_sysctl_table+0x24/0x30 fs/proc/proc_sysctl.c:1753
neigh_sysctl_unregister+0x5f/0x80 net/core/neighbour.c:3895
addrconf_notify+0x140/0x17b0 net/ipv6/addrconf.c:3684
notifier_call_chain+0xbe/0x210 kernel/notifier.c:87
call_netdevice_notifiers_info+0xb5/0x150 net/core/dev.c:1937
call_netdevice_notifiers_extack net/core/dev.c:1975 [inline]
call_netdevice_notifiers net/core/dev.c:1989 [inline]
dev_change_name+0x3c3/0x870 net/core/dev.c:1211
dev_ifsioc+0x800/0xf70 net/core/dev_ioctl.c:376
dev_ioctl+0x3d9/0xf80 net/core/dev_ioctl.c:542
sock_do_ioctl+0x160/0x260 net/socket.c:1213
sock_ioctl+0x3f9/0x670 net/socket.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:870 [inline]
__se_sys_ioctl fs/ioctl.c:856 [inline]
__x64_sys_ioctl+0x19e/0x210 fs/ioctl.c:856
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
The buggy address belongs to the object at ffff888105b0e400
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 16 bytes inside of
freed 1024-byte region [ffff888105b0e400, ffff888105b0e800)
The buggy address belongs to the physical page:
head:ffffea000416c200 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000010200(slab|head|node=0|zone=2)
raw: 0200000000010200 ffff8881000430c0 ffffea00044c7010 ffffea0004510e10
raw: 0000000000000000 00000000000a000a 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888105b0e300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888105b0e380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888105b0e400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888105b0e480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888105b0e500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
In summary, this patch solves those use-after-free by
1. Re-implement the nfc_llcp_find_local(). The current version does not
grab the reference when getting the local from the linked list. For
example, the llcp_sock_bind() gets the reference like below:
// llcp_sock_bind()
local = nfc_llcp_find_local(dev); // A
..... \
| raceable
..... /
llcp_sock->local = nfc_llcp_local_get(local); // B
There is an apparent race window that one can drop the reference
and free the local object fetched in (A) before (B) gets the reference.
2. Some callers of the nfc_llcp_find_local() do not grab the reference
at all. For example, the nfc_genl_llc_{{get/set}_params/sdreq} functions.
We add the nfc_llcp_local_put() for them. Moreover, we add the necessary
error handling function to put the reference.
3. Add the nfc_llcp_remove_local() helper. The local object is removed
from the linked list in local_release() when all reference is gone. This
patch removes it when nfc_llcp_unregister_device() is called.
Therefore, every caller of nfc_llcp_find_local() will get a reference
even when the nfc_llcp_unregister_device() is called. This promises no
use-after-free for the local object is ever possible.
Fixes: 52feb444a903 ("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support")
Fixes: c7aa12252f51 ("NFC: Take a reference on the LLCP local pointer when creating a socket")
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-06-25 17:10:07 +08:00
|
|
|
int err;
|
2014-02-25 09:18:10 +08:00
|
|
|
|
|
|
|
if (gb_len < 3 || gb_len > NFC_MAX_GT_LEN)
|
|
|
|
return -EINVAL;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2014-02-25 09:18:10 +08:00
|
|
|
local = nfc_llcp_find_local(dev);
|
2011-12-14 23:43:12 +08:00
|
|
|
if (local == NULL) {
|
|
|
|
pr_err("No LLCP device\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(local->remote_gb, 0, NFC_MAX_GT_LEN);
|
|
|
|
memcpy(local->remote_gb, gb, gb_len);
|
|
|
|
local->remote_gb_len = gb_len;
|
|
|
|
|
|
|
|
if (memcmp(local->remote_gb, llcp_magic, 3)) {
|
|
|
|
pr_err("MAC does not support LLCP\n");
|
net: nfc: Fix use-after-free caused by nfc_llcp_find_local
This commit fixes several use-after-free that caused by function
nfc_llcp_find_local(). For example, one UAF can happen when below buggy
time window occurs.
// nfc_genl_llc_get_params | // nfc_unregister_device
|
dev = nfc_get_device(idx); | device_lock(...)
if (!dev) | dev->shutting_down = true;
return -ENODEV; | device_unlock(...);
|
device_lock(...); | // nfc_llcp_unregister_device
| nfc_llcp_find_local()
nfc_llcp_find_local(...); |
| local_cleanup()
if (!local) { |
rc = -ENODEV; | // nfc_llcp_local_put
goto exit; | kref_put(.., local_release)
} |
| // local_release
| list_del(&local->list)
// nfc_genl_send_params | kfree()
local->dev->idx !!!UAF!!! |
|
and the crash trace for the one of the discussed UAF like:
BUG: KASAN: slab-use-after-free in nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
Read of size 8 at addr ffff888105b0e410 by task 20114
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:319 [inline]
print_report+0xcc/0x620 mm/kasan/report.c:430
kasan_report+0xb2/0xe0 mm/kasan/report.c:536
nfc_genl_send_params net/nfc/netlink.c:999 [inline]
nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
genl_family_rcv_msg_doit.isra.0+0x1ee/0x2e0 net/netlink/genetlink.c:968
genl_family_rcv_msg net/netlink/genetlink.c:1048 [inline]
genl_rcv_msg+0x503/0x7d0 net/netlink/genetlink.c:1065
netlink_rcv_skb+0x161/0x430 net/netlink/af_netlink.c:2548
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1076
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x644/0x900 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x934/0xe70 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg+0x1b6/0x200 net/socket.c:747
____sys_sendmsg+0x6e9/0x890 net/socket.c:2501
___sys_sendmsg+0x110/0x1b0 net/socket.c:2555
__sys_sendmsg+0xf7/0x1d0 net/socket.c:2584
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
RIP: 0033:0x7f34640a2389
RSP: 002b:00007f3463415168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f34641c1f80 RCX: 00007f34640a2389
RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000006
RBP: 00007f34640ed493 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffe38449ecf R14: 00007f3463415300 R15: 0000000000022000
</TASK>
Allocated by task 20116:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
____kasan_kmalloc mm/kasan/common.c:374 [inline]
__kasan_kmalloc+0x7f/0x90 mm/kasan/common.c:383
kmalloc include/linux/slab.h:580 [inline]
kzalloc include/linux/slab.h:720 [inline]
nfc_llcp_register_device+0x49/0xa40 net/nfc/llcp_core.c:1567
nfc_register_device+0x61/0x260 net/nfc/core.c:1124
nci_register_device+0x776/0xb20 net/nfc/nci/core.c:1257
virtual_ncidev_open+0x147/0x230 drivers/nfc/virtual_ncidev.c:148
misc_open+0x379/0x4a0 drivers/char/misc.c:165
chrdev_open+0x26c/0x780 fs/char_dev.c:414
do_dentry_open+0x6c4/0x12a0 fs/open.c:920
do_open fs/namei.c:3560 [inline]
path_openat+0x24fe/0x37e0 fs/namei.c:3715
do_filp_open+0x1ba/0x410 fs/namei.c:3742
do_sys_openat2+0x171/0x4c0 fs/open.c:1356
do_sys_open fs/open.c:1372 [inline]
__do_sys_openat fs/open.c:1388 [inline]
__se_sys_openat fs/open.c:1383 [inline]
__x64_sys_openat+0x143/0x200 fs/open.c:1383
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Freed by task 20115:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521
____kasan_slab_free mm/kasan/common.c:236 [inline]
____kasan_slab_free mm/kasan/common.c:200 [inline]
__kasan_slab_free+0x10a/0x190 mm/kasan/common.c:244
kasan_slab_free include/linux/kasan.h:162 [inline]
slab_free_hook mm/slub.c:1781 [inline]
slab_free_freelist_hook mm/slub.c:1807 [inline]
slab_free mm/slub.c:3787 [inline]
__kmem_cache_free+0x7a/0x190 mm/slub.c:3800
local_release net/nfc/llcp_core.c:174 [inline]
kref_put include/linux/kref.h:65 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:182 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:177 [inline]
nfc_llcp_unregister_device+0x206/0x290 net/nfc/llcp_core.c:1620
nfc_unregister_device+0x160/0x1d0 net/nfc/core.c:1179
virtual_ncidev_close+0x52/0xa0 drivers/nfc/virtual_ncidev.c:163
__fput+0x252/0xa20 fs/file_table.c:321
task_work_run+0x174/0x270 kernel/task_work.c:179
resume_user_mode_work include/linux/resume_user_mode.h:49 [inline]
exit_to_user_mode_loop kernel/entry/common.c:171 [inline]
exit_to_user_mode_prepare+0x108/0x110 kernel/entry/common.c:204
__syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline]
syscall_exit_to_user_mode+0x21/0x50 kernel/entry/common.c:297
do_syscall_64+0x4c/0x90 arch/x86/entry/common.c:86
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Last potentially related work creation:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
__kasan_record_aux_stack+0x95/0xb0 mm/kasan/generic.c:491
kvfree_call_rcu+0x29/0xa80 kernel/rcu/tree.c:3328
drop_sysctl_table+0x3be/0x4e0 fs/proc/proc_sysctl.c:1735
unregister_sysctl_table.part.0+0x9c/0x190 fs/proc/proc_sysctl.c:1773
unregister_sysctl_table+0x24/0x30 fs/proc/proc_sysctl.c:1753
neigh_sysctl_unregister+0x5f/0x80 net/core/neighbour.c:3895
addrconf_notify+0x140/0x17b0 net/ipv6/addrconf.c:3684
notifier_call_chain+0xbe/0x210 kernel/notifier.c:87
call_netdevice_notifiers_info+0xb5/0x150 net/core/dev.c:1937
call_netdevice_notifiers_extack net/core/dev.c:1975 [inline]
call_netdevice_notifiers net/core/dev.c:1989 [inline]
dev_change_name+0x3c3/0x870 net/core/dev.c:1211
dev_ifsioc+0x800/0xf70 net/core/dev_ioctl.c:376
dev_ioctl+0x3d9/0xf80 net/core/dev_ioctl.c:542
sock_do_ioctl+0x160/0x260 net/socket.c:1213
sock_ioctl+0x3f9/0x670 net/socket.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:870 [inline]
__se_sys_ioctl fs/ioctl.c:856 [inline]
__x64_sys_ioctl+0x19e/0x210 fs/ioctl.c:856
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
The buggy address belongs to the object at ffff888105b0e400
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 16 bytes inside of
freed 1024-byte region [ffff888105b0e400, ffff888105b0e800)
The buggy address belongs to the physical page:
head:ffffea000416c200 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000010200(slab|head|node=0|zone=2)
raw: 0200000000010200 ffff8881000430c0 ffffea00044c7010 ffffea0004510e10
raw: 0000000000000000 00000000000a000a 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888105b0e300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888105b0e380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888105b0e400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888105b0e480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888105b0e500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
In summary, this patch solves those use-after-free by
1. Re-implement the nfc_llcp_find_local(). The current version does not
grab the reference when getting the local from the linked list. For
example, the llcp_sock_bind() gets the reference like below:
// llcp_sock_bind()
local = nfc_llcp_find_local(dev); // A
..... \
| raceable
..... /
llcp_sock->local = nfc_llcp_local_get(local); // B
There is an apparent race window that one can drop the reference
and free the local object fetched in (A) before (B) gets the reference.
2. Some callers of the nfc_llcp_find_local() do not grab the reference
at all. For example, the nfc_genl_llc_{{get/set}_params/sdreq} functions.
We add the nfc_llcp_local_put() for them. Moreover, we add the necessary
error handling function to put the reference.
3. Add the nfc_llcp_remove_local() helper. The local object is removed
from the linked list in local_release() when all reference is gone. This
patch removes it when nfc_llcp_unregister_device() is called.
Therefore, every caller of nfc_llcp_find_local() will get a reference
even when the nfc_llcp_unregister_device() is called. This promises no
use-after-free for the local object is ever possible.
Fixes: 52feb444a903 ("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support")
Fixes: c7aa12252f51 ("NFC: Take a reference on the LLCP local pointer when creating a socket")
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-06-25 17:10:07 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
net: nfc: Fix use-after-free caused by nfc_llcp_find_local
This commit fixes several use-after-free that caused by function
nfc_llcp_find_local(). For example, one UAF can happen when below buggy
time window occurs.
// nfc_genl_llc_get_params | // nfc_unregister_device
|
dev = nfc_get_device(idx); | device_lock(...)
if (!dev) | dev->shutting_down = true;
return -ENODEV; | device_unlock(...);
|
device_lock(...); | // nfc_llcp_unregister_device
| nfc_llcp_find_local()
nfc_llcp_find_local(...); |
| local_cleanup()
if (!local) { |
rc = -ENODEV; | // nfc_llcp_local_put
goto exit; | kref_put(.., local_release)
} |
| // local_release
| list_del(&local->list)
// nfc_genl_send_params | kfree()
local->dev->idx !!!UAF!!! |
|
and the crash trace for the one of the discussed UAF like:
BUG: KASAN: slab-use-after-free in nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
Read of size 8 at addr ffff888105b0e410 by task 20114
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:319 [inline]
print_report+0xcc/0x620 mm/kasan/report.c:430
kasan_report+0xb2/0xe0 mm/kasan/report.c:536
nfc_genl_send_params net/nfc/netlink.c:999 [inline]
nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
genl_family_rcv_msg_doit.isra.0+0x1ee/0x2e0 net/netlink/genetlink.c:968
genl_family_rcv_msg net/netlink/genetlink.c:1048 [inline]
genl_rcv_msg+0x503/0x7d0 net/netlink/genetlink.c:1065
netlink_rcv_skb+0x161/0x430 net/netlink/af_netlink.c:2548
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1076
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x644/0x900 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x934/0xe70 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg+0x1b6/0x200 net/socket.c:747
____sys_sendmsg+0x6e9/0x890 net/socket.c:2501
___sys_sendmsg+0x110/0x1b0 net/socket.c:2555
__sys_sendmsg+0xf7/0x1d0 net/socket.c:2584
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
RIP: 0033:0x7f34640a2389
RSP: 002b:00007f3463415168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f34641c1f80 RCX: 00007f34640a2389
RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000006
RBP: 00007f34640ed493 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffe38449ecf R14: 00007f3463415300 R15: 0000000000022000
</TASK>
Allocated by task 20116:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
____kasan_kmalloc mm/kasan/common.c:374 [inline]
__kasan_kmalloc+0x7f/0x90 mm/kasan/common.c:383
kmalloc include/linux/slab.h:580 [inline]
kzalloc include/linux/slab.h:720 [inline]
nfc_llcp_register_device+0x49/0xa40 net/nfc/llcp_core.c:1567
nfc_register_device+0x61/0x260 net/nfc/core.c:1124
nci_register_device+0x776/0xb20 net/nfc/nci/core.c:1257
virtual_ncidev_open+0x147/0x230 drivers/nfc/virtual_ncidev.c:148
misc_open+0x379/0x4a0 drivers/char/misc.c:165
chrdev_open+0x26c/0x780 fs/char_dev.c:414
do_dentry_open+0x6c4/0x12a0 fs/open.c:920
do_open fs/namei.c:3560 [inline]
path_openat+0x24fe/0x37e0 fs/namei.c:3715
do_filp_open+0x1ba/0x410 fs/namei.c:3742
do_sys_openat2+0x171/0x4c0 fs/open.c:1356
do_sys_open fs/open.c:1372 [inline]
__do_sys_openat fs/open.c:1388 [inline]
__se_sys_openat fs/open.c:1383 [inline]
__x64_sys_openat+0x143/0x200 fs/open.c:1383
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Freed by task 20115:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521
____kasan_slab_free mm/kasan/common.c:236 [inline]
____kasan_slab_free mm/kasan/common.c:200 [inline]
__kasan_slab_free+0x10a/0x190 mm/kasan/common.c:244
kasan_slab_free include/linux/kasan.h:162 [inline]
slab_free_hook mm/slub.c:1781 [inline]
slab_free_freelist_hook mm/slub.c:1807 [inline]
slab_free mm/slub.c:3787 [inline]
__kmem_cache_free+0x7a/0x190 mm/slub.c:3800
local_release net/nfc/llcp_core.c:174 [inline]
kref_put include/linux/kref.h:65 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:182 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:177 [inline]
nfc_llcp_unregister_device+0x206/0x290 net/nfc/llcp_core.c:1620
nfc_unregister_device+0x160/0x1d0 net/nfc/core.c:1179
virtual_ncidev_close+0x52/0xa0 drivers/nfc/virtual_ncidev.c:163
__fput+0x252/0xa20 fs/file_table.c:321
task_work_run+0x174/0x270 kernel/task_work.c:179
resume_user_mode_work include/linux/resume_user_mode.h:49 [inline]
exit_to_user_mode_loop kernel/entry/common.c:171 [inline]
exit_to_user_mode_prepare+0x108/0x110 kernel/entry/common.c:204
__syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline]
syscall_exit_to_user_mode+0x21/0x50 kernel/entry/common.c:297
do_syscall_64+0x4c/0x90 arch/x86/entry/common.c:86
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Last potentially related work creation:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
__kasan_record_aux_stack+0x95/0xb0 mm/kasan/generic.c:491
kvfree_call_rcu+0x29/0xa80 kernel/rcu/tree.c:3328
drop_sysctl_table+0x3be/0x4e0 fs/proc/proc_sysctl.c:1735
unregister_sysctl_table.part.0+0x9c/0x190 fs/proc/proc_sysctl.c:1773
unregister_sysctl_table+0x24/0x30 fs/proc/proc_sysctl.c:1753
neigh_sysctl_unregister+0x5f/0x80 net/core/neighbour.c:3895
addrconf_notify+0x140/0x17b0 net/ipv6/addrconf.c:3684
notifier_call_chain+0xbe/0x210 kernel/notifier.c:87
call_netdevice_notifiers_info+0xb5/0x150 net/core/dev.c:1937
call_netdevice_notifiers_extack net/core/dev.c:1975 [inline]
call_netdevice_notifiers net/core/dev.c:1989 [inline]
dev_change_name+0x3c3/0x870 net/core/dev.c:1211
dev_ifsioc+0x800/0xf70 net/core/dev_ioctl.c:376
dev_ioctl+0x3d9/0xf80 net/core/dev_ioctl.c:542
sock_do_ioctl+0x160/0x260 net/socket.c:1213
sock_ioctl+0x3f9/0x670 net/socket.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:870 [inline]
__se_sys_ioctl fs/ioctl.c:856 [inline]
__x64_sys_ioctl+0x19e/0x210 fs/ioctl.c:856
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
The buggy address belongs to the object at ffff888105b0e400
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 16 bytes inside of
freed 1024-byte region [ffff888105b0e400, ffff888105b0e800)
The buggy address belongs to the physical page:
head:ffffea000416c200 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000010200(slab|head|node=0|zone=2)
raw: 0200000000010200 ffff8881000430c0 ffffea00044c7010 ffffea0004510e10
raw: 0000000000000000 00000000000a000a 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888105b0e300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888105b0e380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888105b0e400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888105b0e480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888105b0e500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
In summary, this patch solves those use-after-free by
1. Re-implement the nfc_llcp_find_local(). The current version does not
grab the reference when getting the local from the linked list. For
example, the llcp_sock_bind() gets the reference like below:
// llcp_sock_bind()
local = nfc_llcp_find_local(dev); // A
..... \
| raceable
..... /
llcp_sock->local = nfc_llcp_local_get(local); // B
There is an apparent race window that one can drop the reference
and free the local object fetched in (A) before (B) gets the reference.
2. Some callers of the nfc_llcp_find_local() do not grab the reference
at all. For example, the nfc_genl_llc_{{get/set}_params/sdreq} functions.
We add the nfc_llcp_local_put() for them. Moreover, we add the necessary
error handling function to put the reference.
3. Add the nfc_llcp_remove_local() helper. The local object is removed
from the linked list in local_release() when all reference is gone. This
patch removes it when nfc_llcp_unregister_device() is called.
Therefore, every caller of nfc_llcp_find_local() will get a reference
even when the nfc_llcp_unregister_device() is called. This promises no
use-after-free for the local object is ever possible.
Fixes: 52feb444a903 ("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support")
Fixes: c7aa12252f51 ("NFC: Take a reference on the LLCP local pointer when creating a socket")
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-06-25 17:10:07 +08:00
|
|
|
err = nfc_llcp_parse_gb_tlv(local,
|
2012-05-08 04:03:34 +08:00
|
|
|
&local->remote_gb[3],
|
|
|
|
local->remote_gb_len - 3);
|
net: nfc: Fix use-after-free caused by nfc_llcp_find_local
This commit fixes several use-after-free that caused by function
nfc_llcp_find_local(). For example, one UAF can happen when below buggy
time window occurs.
// nfc_genl_llc_get_params | // nfc_unregister_device
|
dev = nfc_get_device(idx); | device_lock(...)
if (!dev) | dev->shutting_down = true;
return -ENODEV; | device_unlock(...);
|
device_lock(...); | // nfc_llcp_unregister_device
| nfc_llcp_find_local()
nfc_llcp_find_local(...); |
| local_cleanup()
if (!local) { |
rc = -ENODEV; | // nfc_llcp_local_put
goto exit; | kref_put(.., local_release)
} |
| // local_release
| list_del(&local->list)
// nfc_genl_send_params | kfree()
local->dev->idx !!!UAF!!! |
|
and the crash trace for the one of the discussed UAF like:
BUG: KASAN: slab-use-after-free in nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
Read of size 8 at addr ffff888105b0e410 by task 20114
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:319 [inline]
print_report+0xcc/0x620 mm/kasan/report.c:430
kasan_report+0xb2/0xe0 mm/kasan/report.c:536
nfc_genl_send_params net/nfc/netlink.c:999 [inline]
nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
genl_family_rcv_msg_doit.isra.0+0x1ee/0x2e0 net/netlink/genetlink.c:968
genl_family_rcv_msg net/netlink/genetlink.c:1048 [inline]
genl_rcv_msg+0x503/0x7d0 net/netlink/genetlink.c:1065
netlink_rcv_skb+0x161/0x430 net/netlink/af_netlink.c:2548
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1076
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x644/0x900 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x934/0xe70 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg+0x1b6/0x200 net/socket.c:747
____sys_sendmsg+0x6e9/0x890 net/socket.c:2501
___sys_sendmsg+0x110/0x1b0 net/socket.c:2555
__sys_sendmsg+0xf7/0x1d0 net/socket.c:2584
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
RIP: 0033:0x7f34640a2389
RSP: 002b:00007f3463415168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f34641c1f80 RCX: 00007f34640a2389
RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000006
RBP: 00007f34640ed493 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffe38449ecf R14: 00007f3463415300 R15: 0000000000022000
</TASK>
Allocated by task 20116:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
____kasan_kmalloc mm/kasan/common.c:374 [inline]
__kasan_kmalloc+0x7f/0x90 mm/kasan/common.c:383
kmalloc include/linux/slab.h:580 [inline]
kzalloc include/linux/slab.h:720 [inline]
nfc_llcp_register_device+0x49/0xa40 net/nfc/llcp_core.c:1567
nfc_register_device+0x61/0x260 net/nfc/core.c:1124
nci_register_device+0x776/0xb20 net/nfc/nci/core.c:1257
virtual_ncidev_open+0x147/0x230 drivers/nfc/virtual_ncidev.c:148
misc_open+0x379/0x4a0 drivers/char/misc.c:165
chrdev_open+0x26c/0x780 fs/char_dev.c:414
do_dentry_open+0x6c4/0x12a0 fs/open.c:920
do_open fs/namei.c:3560 [inline]
path_openat+0x24fe/0x37e0 fs/namei.c:3715
do_filp_open+0x1ba/0x410 fs/namei.c:3742
do_sys_openat2+0x171/0x4c0 fs/open.c:1356
do_sys_open fs/open.c:1372 [inline]
__do_sys_openat fs/open.c:1388 [inline]
__se_sys_openat fs/open.c:1383 [inline]
__x64_sys_openat+0x143/0x200 fs/open.c:1383
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Freed by task 20115:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521
____kasan_slab_free mm/kasan/common.c:236 [inline]
____kasan_slab_free mm/kasan/common.c:200 [inline]
__kasan_slab_free+0x10a/0x190 mm/kasan/common.c:244
kasan_slab_free include/linux/kasan.h:162 [inline]
slab_free_hook mm/slub.c:1781 [inline]
slab_free_freelist_hook mm/slub.c:1807 [inline]
slab_free mm/slub.c:3787 [inline]
__kmem_cache_free+0x7a/0x190 mm/slub.c:3800
local_release net/nfc/llcp_core.c:174 [inline]
kref_put include/linux/kref.h:65 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:182 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:177 [inline]
nfc_llcp_unregister_device+0x206/0x290 net/nfc/llcp_core.c:1620
nfc_unregister_device+0x160/0x1d0 net/nfc/core.c:1179
virtual_ncidev_close+0x52/0xa0 drivers/nfc/virtual_ncidev.c:163
__fput+0x252/0xa20 fs/file_table.c:321
task_work_run+0x174/0x270 kernel/task_work.c:179
resume_user_mode_work include/linux/resume_user_mode.h:49 [inline]
exit_to_user_mode_loop kernel/entry/common.c:171 [inline]
exit_to_user_mode_prepare+0x108/0x110 kernel/entry/common.c:204
__syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline]
syscall_exit_to_user_mode+0x21/0x50 kernel/entry/common.c:297
do_syscall_64+0x4c/0x90 arch/x86/entry/common.c:86
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Last potentially related work creation:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
__kasan_record_aux_stack+0x95/0xb0 mm/kasan/generic.c:491
kvfree_call_rcu+0x29/0xa80 kernel/rcu/tree.c:3328
drop_sysctl_table+0x3be/0x4e0 fs/proc/proc_sysctl.c:1735
unregister_sysctl_table.part.0+0x9c/0x190 fs/proc/proc_sysctl.c:1773
unregister_sysctl_table+0x24/0x30 fs/proc/proc_sysctl.c:1753
neigh_sysctl_unregister+0x5f/0x80 net/core/neighbour.c:3895
addrconf_notify+0x140/0x17b0 net/ipv6/addrconf.c:3684
notifier_call_chain+0xbe/0x210 kernel/notifier.c:87
call_netdevice_notifiers_info+0xb5/0x150 net/core/dev.c:1937
call_netdevice_notifiers_extack net/core/dev.c:1975 [inline]
call_netdevice_notifiers net/core/dev.c:1989 [inline]
dev_change_name+0x3c3/0x870 net/core/dev.c:1211
dev_ifsioc+0x800/0xf70 net/core/dev_ioctl.c:376
dev_ioctl+0x3d9/0xf80 net/core/dev_ioctl.c:542
sock_do_ioctl+0x160/0x260 net/socket.c:1213
sock_ioctl+0x3f9/0x670 net/socket.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:870 [inline]
__se_sys_ioctl fs/ioctl.c:856 [inline]
__x64_sys_ioctl+0x19e/0x210 fs/ioctl.c:856
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
The buggy address belongs to the object at ffff888105b0e400
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 16 bytes inside of
freed 1024-byte region [ffff888105b0e400, ffff888105b0e800)
The buggy address belongs to the physical page:
head:ffffea000416c200 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000010200(slab|head|node=0|zone=2)
raw: 0200000000010200 ffff8881000430c0 ffffea00044c7010 ffffea0004510e10
raw: 0000000000000000 00000000000a000a 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888105b0e300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888105b0e380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888105b0e400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888105b0e480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888105b0e500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
In summary, this patch solves those use-after-free by
1. Re-implement the nfc_llcp_find_local(). The current version does not
grab the reference when getting the local from the linked list. For
example, the llcp_sock_bind() gets the reference like below:
// llcp_sock_bind()
local = nfc_llcp_find_local(dev); // A
..... \
| raceable
..... /
llcp_sock->local = nfc_llcp_local_get(local); // B
There is an apparent race window that one can drop the reference
and free the local object fetched in (A) before (B) gets the reference.
2. Some callers of the nfc_llcp_find_local() do not grab the reference
at all. For example, the nfc_genl_llc_{{get/set}_params/sdreq} functions.
We add the nfc_llcp_local_put() for them. Moreover, we add the necessary
error handling function to put the reference.
3. Add the nfc_llcp_remove_local() helper. The local object is removed
from the linked list in local_release() when all reference is gone. This
patch removes it when nfc_llcp_unregister_device() is called.
Therefore, every caller of nfc_llcp_find_local() will get a reference
even when the nfc_llcp_unregister_device() is called. This promises no
use-after-free for the local object is ever possible.
Fixes: 52feb444a903 ("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support")
Fixes: c7aa12252f51 ("NFC: Take a reference on the LLCP local pointer when creating a socket")
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-06-25 17:10:07 +08:00
|
|
|
out:
|
|
|
|
nfc_llcp_local_put(local);
|
|
|
|
return err;
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
2021-07-30 22:41:59 +08:00
|
|
|
static u8 nfc_llcp_dsap(const struct sk_buff *pdu)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
|
|
|
return (pdu->data[0] & 0xfc) >> 2;
|
|
|
|
}
|
|
|
|
|
2021-07-30 22:41:59 +08:00
|
|
|
static u8 nfc_llcp_ptype(const struct sk_buff *pdu)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
|
|
|
return ((pdu->data[0] & 0x03) << 2) | ((pdu->data[1] & 0xc0) >> 6);
|
|
|
|
}
|
|
|
|
|
2021-07-30 22:41:59 +08:00
|
|
|
static u8 nfc_llcp_ssap(const struct sk_buff *pdu)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
|
|
|
return pdu->data[1] & 0x3f;
|
|
|
|
}
|
|
|
|
|
2021-07-30 22:41:59 +08:00
|
|
|
static u8 nfc_llcp_ns(const struct sk_buff *pdu)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
|
|
|
return pdu->data[2] >> 4;
|
|
|
|
}
|
|
|
|
|
2021-07-30 22:41:59 +08:00
|
|
|
static u8 nfc_llcp_nr(const struct sk_buff *pdu)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
|
|
|
return pdu->data[2] & 0xf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfc_llcp_set_nrns(struct nfc_llcp_sock *sock, struct sk_buff *pdu)
|
|
|
|
{
|
2012-04-11 01:43:14 +08:00
|
|
|
pdu->data[2] = (sock->send_n << 4) | (sock->recv_n);
|
2011-12-14 23:43:12 +08:00
|
|
|
sock->send_n = (sock->send_n + 1) % 16;
|
|
|
|
sock->recv_ack_n = (sock->recv_n - 1) % 16;
|
|
|
|
}
|
|
|
|
|
2012-09-27 00:16:44 +08:00
|
|
|
void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local,
|
|
|
|
struct sk_buff *skb, u8 direction)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb_copy = NULL, *nskb;
|
|
|
|
struct sock *sk;
|
|
|
|
u8 *data;
|
|
|
|
|
|
|
|
read_lock(&local->raw_sockets.lock);
|
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
sk_for_each(sk, &local->raw_sockets.head) {
|
2012-09-27 00:16:44 +08:00
|
|
|
if (sk->sk_state != LLCP_BOUND)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (skb_copy == NULL) {
|
2014-06-12 06:36:26 +08:00
|
|
|
skb_copy = __pskb_copy_fclone(skb, NFC_RAW_HEADER_SIZE,
|
|
|
|
GFP_ATOMIC, true);
|
2012-09-27 00:16:44 +08:00
|
|
|
|
|
|
|
if (skb_copy == NULL)
|
|
|
|
continue;
|
|
|
|
|
2014-05-05 18:43:31 +08:00
|
|
|
data = skb_push(skb_copy, NFC_RAW_HEADER_SIZE);
|
2012-09-27 00:16:44 +08:00
|
|
|
|
|
|
|
data[0] = local->dev ? local->dev->idx : 0xFF;
|
2014-05-05 18:43:31 +08:00
|
|
|
data[1] = direction & 0x01;
|
|
|
|
data[1] |= (RAW_PAYLOAD_LLCP << 1);
|
2012-09-27 00:16:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
nskb = skb_clone(skb_copy, GFP_ATOMIC);
|
|
|
|
if (!nskb)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (sock_queue_rcv_skb(sk, nskb))
|
|
|
|
kfree_skb(nskb);
|
|
|
|
}
|
|
|
|
|
|
|
|
read_unlock(&local->raw_sockets.lock);
|
|
|
|
|
|
|
|
kfree_skb(skb_copy);
|
|
|
|
}
|
|
|
|
|
2012-05-30 23:48:29 +08:00
|
|
|
static void nfc_llcp_tx_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
|
|
|
|
tx_work);
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct sock *sk;
|
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
|
|
|
|
|
|
|
skb = skb_dequeue(&local->tx_queue);
|
|
|
|
if (skb != NULL) {
|
|
|
|
sk = skb->sk;
|
|
|
|
llcp_sock = nfc_llcp_sock(sk);
|
2012-10-05 06:37:22 +08:00
|
|
|
|
|
|
|
if (llcp_sock == NULL && nfc_llcp_ptype(skb) == LLCP_PDU_I) {
|
2013-06-05 23:15:59 +08:00
|
|
|
kfree_skb(skb);
|
2012-10-05 06:37:22 +08:00
|
|
|
nfc_llcp_send_symm(local->dev);
|
2013-05-28 21:03:17 +08:00
|
|
|
} else if (llcp_sock && !llcp_sock->remote_ready) {
|
|
|
|
skb_queue_head(&local->tx_queue, skb);
|
|
|
|
nfc_llcp_send_symm(local->dev);
|
2012-10-05 06:37:22 +08:00
|
|
|
} else {
|
2012-11-02 06:36:07 +08:00
|
|
|
struct sk_buff *copy_skb = NULL;
|
|
|
|
u8 ptype = nfc_llcp_ptype(skb);
|
2012-05-30 23:48:29 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
pr_debug("Sending pending skb\n");
|
2016-06-07 22:21:53 +08:00
|
|
|
print_hex_dump_debug("LLCP Tx: ", DUMP_PREFIX_OFFSET,
|
|
|
|
16, 1, skb->data, skb->len, true);
|
2012-05-30 23:48:29 +08:00
|
|
|
|
2012-11-02 06:36:07 +08:00
|
|
|
if (ptype == LLCP_PDU_I)
|
|
|
|
copy_skb = skb_copy(skb, GFP_ATOMIC);
|
|
|
|
|
2012-11-27 22:44:24 +08:00
|
|
|
__net_timestamp(skb);
|
|
|
|
|
2012-09-27 00:16:44 +08:00
|
|
|
nfc_llcp_send_to_raw_sock(local, skb,
|
2014-05-05 18:43:31 +08:00
|
|
|
NFC_DIRECTION_TX);
|
2012-09-27 00:16:44 +08:00
|
|
|
|
2012-05-30 23:48:29 +08:00
|
|
|
ret = nfc_data_exchange(local->dev, local->target_idx,
|
|
|
|
skb, nfc_llcp_recv, local);
|
|
|
|
|
2012-11-02 06:36:07 +08:00
|
|
|
if (ret) {
|
|
|
|
kfree_skb(copy_skb);
|
|
|
|
goto out;
|
2012-05-30 23:48:29 +08:00
|
|
|
}
|
2012-11-02 06:36:07 +08:00
|
|
|
|
|
|
|
if (ptype == LLCP_PDU_I && copy_skb)
|
|
|
|
skb_queue_tail(&llcp_sock->tx_pending_queue,
|
|
|
|
copy_skb);
|
2012-05-30 23:48:29 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
nfc_llcp_send_symm(local->dev);
|
|
|
|
}
|
|
|
|
|
2012-11-02 06:36:07 +08:00
|
|
|
out:
|
2012-05-30 23:48:29 +08:00
|
|
|
mod_timer(&local->link_timer,
|
|
|
|
jiffies + msecs_to_jiffies(2 * local->remote_lto));
|
|
|
|
}
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
static struct nfc_llcp_sock *nfc_llcp_connecting_sock_get(struct nfc_llcp_local *local,
|
|
|
|
u8 ssap)
|
|
|
|
{
|
|
|
|
struct sock *sk;
|
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
|
|
|
|
|
|
|
read_lock(&local->connecting_sockets.lock);
|
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
sk_for_each(sk, &local->connecting_sockets.head) {
|
2012-05-04 23:04:19 +08:00
|
|
|
llcp_sock = nfc_llcp_sock(sk);
|
|
|
|
|
2012-05-21 17:44:42 +08:00
|
|
|
if (llcp_sock->ssap == ssap) {
|
|
|
|
sock_hold(&llcp_sock->sk);
|
2012-05-04 23:04:19 +08:00
|
|
|
goto out;
|
2012-05-21 17:44:42 +08:00
|
|
|
}
|
2012-05-04 23:04:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
llcp_sock = NULL;
|
|
|
|
|
|
|
|
out:
|
|
|
|
read_unlock(&local->connecting_sockets.lock);
|
|
|
|
|
|
|
|
return llcp_sock;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct nfc_llcp_sock *nfc_llcp_sock_get_sn(struct nfc_llcp_local *local,
|
2021-07-30 22:41:59 +08:00
|
|
|
const u8 *sn, size_t sn_len)
|
2012-05-04 23:04:19 +08:00
|
|
|
{
|
2023-10-09 20:31:10 +08:00
|
|
|
return nfc_llcp_sock_from_sn(local, sn, sn_len, true);
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
2021-07-30 22:41:59 +08:00
|
|
|
static const u8 *nfc_llcp_connect_sn(const struct sk_buff *skb, size_t *sn_len)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
2021-07-30 22:41:59 +08:00
|
|
|
u8 type, length;
|
|
|
|
const u8 *tlv = &skb->data[2];
|
2011-12-14 23:43:12 +08:00
|
|
|
size_t tlv_array_len = skb->len - LLCP_HEADER_SIZE, offset = 0;
|
|
|
|
|
|
|
|
while (offset < tlv_array_len) {
|
|
|
|
type = tlv[0];
|
|
|
|
length = tlv[1];
|
|
|
|
|
|
|
|
pr_debug("type 0x%x length %d\n", type, length);
|
|
|
|
|
|
|
|
if (type == LLCP_TLV_SN) {
|
|
|
|
*sn_len = length;
|
|
|
|
return &tlv[2];
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += length + 2;
|
|
|
|
tlv += length + 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-10-15 22:14:37 +08:00
|
|
|
static void nfc_llcp_recv_ui(struct nfc_llcp_local *local,
|
|
|
|
struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
|
|
|
struct nfc_llcp_ui_cb *ui_cb;
|
|
|
|
u8 dsap, ssap;
|
|
|
|
|
|
|
|
dsap = nfc_llcp_dsap(skb);
|
|
|
|
ssap = nfc_llcp_ssap(skb);
|
|
|
|
|
|
|
|
ui_cb = nfc_llcp_ui_skb_cb(skb);
|
|
|
|
ui_cb->dsap = dsap;
|
|
|
|
ui_cb->ssap = ssap;
|
|
|
|
|
|
|
|
pr_debug("%d %d\n", dsap, ssap);
|
|
|
|
|
|
|
|
/* We're looking for a bound socket, not a client one */
|
|
|
|
llcp_sock = nfc_llcp_sock_get(local, dsap, LLCP_SAP_SDP);
|
|
|
|
if (llcp_sock == NULL || llcp_sock->sk.sk_type != SOCK_DGRAM)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* There is no sequence with UI frames */
|
|
|
|
skb_pull(skb, LLCP_HEADER_SIZE);
|
2012-12-07 23:37:30 +08:00
|
|
|
if (!sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
|
|
|
|
/*
|
|
|
|
* UI frames will be freed from the socket layer, so we
|
|
|
|
* need to keep them alive until someone receives them.
|
|
|
|
*/
|
|
|
|
skb_get(skb);
|
|
|
|
} else {
|
|
|
|
pr_err("Receive queue is full\n");
|
2012-10-15 22:14:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
nfc_llcp_sock_put(llcp_sock);
|
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
|
2021-07-30 22:41:59 +08:00
|
|
|
const struct sk_buff *skb)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
|
|
|
struct sock *new_sk, *parent;
|
|
|
|
struct nfc_llcp_sock *sock, *new_sock;
|
2012-05-04 23:04:19 +08:00
|
|
|
u8 dsap, ssap, reason;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
dsap = nfc_llcp_dsap(skb);
|
|
|
|
ssap = nfc_llcp_ssap(skb);
|
|
|
|
|
|
|
|
pr_debug("%d %d\n", dsap, ssap);
|
|
|
|
|
|
|
|
if (dsap != LLCP_SAP_SDP) {
|
2012-05-04 23:04:19 +08:00
|
|
|
sock = nfc_llcp_sock_get(local, dsap, LLCP_SAP_SDP);
|
|
|
|
if (sock == NULL || sock->sk.sk_state != LLCP_LISTEN) {
|
2011-12-14 23:43:12 +08:00
|
|
|
reason = LLCP_DM_NOBOUND;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
} else {
|
2021-07-30 22:41:59 +08:00
|
|
|
const u8 *sn;
|
2011-12-14 23:43:12 +08:00
|
|
|
size_t sn_len;
|
|
|
|
|
|
|
|
sn = nfc_llcp_connect_sn(skb, &sn_len);
|
|
|
|
if (sn == NULL) {
|
|
|
|
reason = LLCP_DM_NOBOUND;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_debug("Service name length %zu\n", sn_len);
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
sock = nfc_llcp_sock_get_sn(local, sn, sn_len);
|
|
|
|
if (sock == NULL) {
|
|
|
|
reason = LLCP_DM_NOBOUND;
|
|
|
|
goto fail;
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
lock_sock(&sock->sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
parent = &sock->sk;
|
|
|
|
|
|
|
|
if (sk_acceptq_is_full(parent)) {
|
|
|
|
reason = LLCP_DM_REJ;
|
|
|
|
release_sock(&sock->sk);
|
|
|
|
sock_put(&sock->sk);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2012-06-25 21:46:28 +08:00
|
|
|
if (sock->ssap == LLCP_SDP_UNBOUND) {
|
|
|
|
u8 ssap = nfc_llcp_reserve_sdp_ssap(local);
|
|
|
|
|
|
|
|
pr_debug("First client, reserving %d\n", ssap);
|
|
|
|
|
|
|
|
if (ssap == LLCP_SAP_MAX) {
|
|
|
|
reason = LLCP_DM_REJ;
|
|
|
|
release_sock(&sock->sk);
|
|
|
|
sock_put(&sock->sk);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
sock->ssap = ssap;
|
|
|
|
}
|
|
|
|
|
2015-05-09 10:09:13 +08:00
|
|
|
new_sk = nfc_llcp_sock_alloc(NULL, parent->sk_type, GFP_ATOMIC, 0);
|
2011-12-14 23:43:12 +08:00
|
|
|
if (new_sk == NULL) {
|
|
|
|
reason = LLCP_DM_REJ;
|
|
|
|
release_sock(&sock->sk);
|
|
|
|
sock_put(&sock->sk);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_sock = nfc_llcp_sock(new_sk);
|
|
|
|
new_sock->dev = local->dev;
|
2012-05-04 17:24:16 +08:00
|
|
|
new_sock->local = nfc_llcp_local_get(local);
|
2013-02-22 18:38:05 +08:00
|
|
|
new_sock->rw = sock->rw;
|
|
|
|
new_sock->miux = sock->miux;
|
2011-12-14 23:43:12 +08:00
|
|
|
new_sock->nfc_protocol = sock->nfc_protocol;
|
|
|
|
new_sock->dsap = ssap;
|
2012-06-19 03:38:09 +08:00
|
|
|
new_sock->target_idx = local->target_idx;
|
2011-12-14 23:43:12 +08:00
|
|
|
new_sock->parent = parent;
|
2012-06-25 21:46:28 +08:00
|
|
|
new_sock->ssap = sock->ssap;
|
|
|
|
if (sock->ssap < LLCP_LOCAL_NUM_SAP && sock->ssap >= LLCP_WKS_NUM_SAP) {
|
|
|
|
atomic_t *client_count;
|
|
|
|
|
|
|
|
pr_debug("reserved_ssap %d for %p\n", sock->ssap, new_sock);
|
|
|
|
|
|
|
|
client_count =
|
|
|
|
&local->local_sdp_cnt[sock->ssap - LLCP_WKS_NUM_SAP];
|
|
|
|
|
|
|
|
atomic_inc(client_count);
|
|
|
|
new_sock->reserved_ssap = sock->ssap;
|
|
|
|
}
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-05-08 04:03:34 +08:00
|
|
|
nfc_llcp_parse_connection_tlv(new_sock, &skb->data[LLCP_HEADER_SIZE],
|
|
|
|
skb->len - LLCP_HEADER_SIZE);
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
pr_debug("new sock %p sk %p\n", new_sock, &new_sock->sk);
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
nfc_llcp_sock_link(&local->sockets, new_sk);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
nfc_llcp_accept_enqueue(&sock->sk, new_sk);
|
|
|
|
|
|
|
|
nfc_get_device(local->dev->idx);
|
|
|
|
|
|
|
|
new_sk->sk_state = LLCP_CONNECTED;
|
|
|
|
|
|
|
|
/* Wake the listening processes */
|
2014-04-12 04:15:36 +08:00
|
|
|
parent->sk_data_ready(parent);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
/* Send CC */
|
|
|
|
nfc_llcp_send_cc(new_sock);
|
|
|
|
|
|
|
|
release_sock(&sock->sk);
|
|
|
|
sock_put(&sock->sk);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
/* Send DM */
|
|
|
|
nfc_llcp_send_dm(local, dsap, ssap, reason);
|
|
|
|
}
|
|
|
|
|
2012-03-05 08:03:42 +08:00
|
|
|
int nfc_llcp_queue_i_frames(struct nfc_llcp_sock *sock)
|
2012-03-05 08:03:35 +08:00
|
|
|
{
|
2012-03-05 08:03:42 +08:00
|
|
|
int nr_frames = 0;
|
2012-03-05 08:03:35 +08:00
|
|
|
struct nfc_llcp_local *local = sock->local;
|
|
|
|
|
|
|
|
pr_debug("Remote ready %d tx queue len %d remote rw %d",
|
2012-03-05 08:03:52 +08:00
|
|
|
sock->remote_ready, skb_queue_len(&sock->tx_pending_queue),
|
2013-02-22 08:12:28 +08:00
|
|
|
sock->remote_rw);
|
2012-03-05 08:03:35 +08:00
|
|
|
|
|
|
|
/* Try to queue some I frames for transmission */
|
|
|
|
while (sock->remote_ready &&
|
2013-02-22 08:12:28 +08:00
|
|
|
skb_queue_len(&sock->tx_pending_queue) < sock->remote_rw) {
|
2012-05-30 23:48:29 +08:00
|
|
|
struct sk_buff *pdu;
|
2012-03-05 08:03:35 +08:00
|
|
|
|
|
|
|
pdu = skb_dequeue(&sock->tx_queue);
|
|
|
|
if (pdu == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Update N(S)/N(R) */
|
|
|
|
nfc_llcp_set_nrns(sock, pdu);
|
|
|
|
|
|
|
|
skb_queue_tail(&local->tx_queue, pdu);
|
2012-03-05 08:03:42 +08:00
|
|
|
nr_frames++;
|
2012-03-05 08:03:35 +08:00
|
|
|
}
|
2012-03-05 08:03:42 +08:00
|
|
|
|
|
|
|
return nr_frames;
|
2012-03-05 08:03:35 +08:00
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
static void nfc_llcp_recv_hdlc(struct nfc_llcp_local *local,
|
2012-03-05 08:03:52 +08:00
|
|
|
struct sk_buff *skb)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
|
|
|
struct sock *sk;
|
|
|
|
u8 dsap, ssap, ptype, ns, nr;
|
|
|
|
|
|
|
|
ptype = nfc_llcp_ptype(skb);
|
|
|
|
dsap = nfc_llcp_dsap(skb);
|
|
|
|
ssap = nfc_llcp_ssap(skb);
|
|
|
|
ns = nfc_llcp_ns(skb);
|
|
|
|
nr = nfc_llcp_nr(skb);
|
|
|
|
|
|
|
|
pr_debug("%d %d R %d S %d\n", dsap, ssap, nr, ns);
|
|
|
|
|
|
|
|
llcp_sock = nfc_llcp_sock_get(local, dsap, ssap);
|
|
|
|
if (llcp_sock == NULL) {
|
|
|
|
nfc_llcp_send_dm(local, dsap, ssap, LLCP_DM_NOCONN);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sk = &llcp_sock->sk;
|
|
|
|
lock_sock(sk);
|
|
|
|
if (sk->sk_state == LLCP_CLOSED) {
|
|
|
|
release_sock(sk);
|
|
|
|
nfc_llcp_sock_put(llcp_sock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Pass the payload upstream */
|
|
|
|
if (ptype == LLCP_PDU_I) {
|
|
|
|
pr_debug("I frame, queueing on %p\n", &llcp_sock->sk);
|
|
|
|
|
2012-03-05 08:03:36 +08:00
|
|
|
if (ns == llcp_sock->recv_n)
|
|
|
|
llcp_sock->recv_n = (llcp_sock->recv_n + 1) % 16;
|
|
|
|
else
|
|
|
|
pr_err("Received out of sequence I PDU\n");
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
skb_pull(skb, LLCP_HEADER_SIZE + LLCP_SEQUENCE_SIZE);
|
2012-12-07 23:37:30 +08:00
|
|
|
if (!sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
|
|
|
|
/*
|
|
|
|
* I frames will be freed from the socket layer, so we
|
|
|
|
* need to keep them alive until someone receives them.
|
|
|
|
*/
|
|
|
|
skb_get(skb);
|
|
|
|
} else {
|
|
|
|
pr_err("Receive queue is full\n");
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove skbs from the pending queue */
|
|
|
|
if (llcp_sock->send_ack_n != nr) {
|
|
|
|
struct sk_buff *s, *tmp;
|
2012-11-26 15:40:04 +08:00
|
|
|
u8 n;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
llcp_sock->send_ack_n = nr;
|
|
|
|
|
2012-05-30 23:48:29 +08:00
|
|
|
/* Remove and free all skbs until ns == nr */
|
|
|
|
skb_queue_walk_safe(&llcp_sock->tx_pending_queue, s, tmp) {
|
2012-11-26 15:40:04 +08:00
|
|
|
n = nfc_llcp_ns(s);
|
|
|
|
|
2012-05-30 23:48:29 +08:00
|
|
|
skb_unlink(s, &llcp_sock->tx_pending_queue);
|
|
|
|
kfree_skb(s);
|
|
|
|
|
2012-11-26 15:40:04 +08:00
|
|
|
if (n == nr)
|
2012-05-30 23:48:29 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Re-queue the remaining skbs for transmission */
|
|
|
|
skb_queue_reverse_walk_safe(&llcp_sock->tx_pending_queue,
|
|
|
|
s, tmp) {
|
|
|
|
skb_unlink(s, &llcp_sock->tx_pending_queue);
|
|
|
|
skb_queue_head(&local->tx_queue, s);
|
|
|
|
}
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
2012-03-05 08:03:36 +08:00
|
|
|
if (ptype == LLCP_PDU_RR)
|
|
|
|
llcp_sock->remote_ready = true;
|
2012-03-05 08:03:52 +08:00
|
|
|
else if (ptype == LLCP_PDU_RNR)
|
2012-03-05 08:03:36 +08:00
|
|
|
llcp_sock->remote_ready = false;
|
|
|
|
|
2012-05-31 00:06:11 +08:00
|
|
|
if (nfc_llcp_queue_i_frames(llcp_sock) == 0 && ptype == LLCP_PDU_I)
|
2012-03-05 08:03:42 +08:00
|
|
|
nfc_llcp_send_rr(llcp_sock);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
release_sock(sk);
|
|
|
|
nfc_llcp_sock_put(llcp_sock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfc_llcp_recv_disc(struct nfc_llcp_local *local,
|
2021-07-30 22:41:59 +08:00
|
|
|
const struct sk_buff *skb)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
|
|
|
struct sock *sk;
|
|
|
|
u8 dsap, ssap;
|
|
|
|
|
|
|
|
dsap = nfc_llcp_dsap(skb);
|
|
|
|
ssap = nfc_llcp_ssap(skb);
|
|
|
|
|
2013-04-03 22:44:44 +08:00
|
|
|
if ((dsap == 0) && (ssap == 0)) {
|
|
|
|
pr_debug("Connection termination");
|
|
|
|
nfc_dep_link_down(local->dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
llcp_sock = nfc_llcp_sock_get(local, dsap, ssap);
|
|
|
|
if (llcp_sock == NULL) {
|
|
|
|
nfc_llcp_send_dm(local, dsap, ssap, LLCP_DM_NOCONN);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sk = &llcp_sock->sk;
|
|
|
|
lock_sock(sk);
|
2012-10-27 00:20:10 +08:00
|
|
|
|
|
|
|
nfc_llcp_socket_purge(llcp_sock);
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
if (sk->sk_state == LLCP_CLOSED) {
|
|
|
|
release_sock(sk);
|
|
|
|
nfc_llcp_sock_put(llcp_sock);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sk->sk_state == LLCP_CONNECTED) {
|
|
|
|
nfc_put_device(local->dev);
|
|
|
|
sk->sk_state = LLCP_CLOSED;
|
|
|
|
sk->sk_state_change(sk);
|
|
|
|
}
|
|
|
|
|
|
|
|
nfc_llcp_send_dm(local, dsap, ssap, LLCP_DM_DISC);
|
|
|
|
|
|
|
|
release_sock(sk);
|
|
|
|
nfc_llcp_sock_put(llcp_sock);
|
|
|
|
}
|
|
|
|
|
2021-07-30 22:41:59 +08:00
|
|
|
static void nfc_llcp_recv_cc(struct nfc_llcp_local *local,
|
|
|
|
const struct sk_buff *skb)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
2012-05-07 18:31:19 +08:00
|
|
|
struct sock *sk;
|
2011-12-14 23:43:12 +08:00
|
|
|
u8 dsap, ssap;
|
|
|
|
|
|
|
|
dsap = nfc_llcp_dsap(skb);
|
|
|
|
ssap = nfc_llcp_ssap(skb);
|
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
llcp_sock = nfc_llcp_connecting_sock_get(local, dsap);
|
2011-12-14 23:43:12 +08:00
|
|
|
if (llcp_sock == NULL) {
|
|
|
|
pr_err("Invalid CC\n");
|
|
|
|
nfc_llcp_send_dm(local, dsap, ssap, LLCP_DM_NOCONN);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-05-07 18:31:19 +08:00
|
|
|
sk = &llcp_sock->sk;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-05-04 23:04:19 +08:00
|
|
|
/* Unlink from connecting and link to the client array */
|
|
|
|
nfc_llcp_sock_unlink(&local->connecting_sockets, sk);
|
|
|
|
nfc_llcp_sock_link(&local->sockets, sk);
|
|
|
|
llcp_sock->dsap = ssap;
|
|
|
|
|
2012-05-08 04:03:34 +08:00
|
|
|
nfc_llcp_parse_connection_tlv(llcp_sock, &skb->data[LLCP_HEADER_SIZE],
|
|
|
|
skb->len - LLCP_HEADER_SIZE);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-05-07 18:31:19 +08:00
|
|
|
sk->sk_state = LLCP_CONNECTED;
|
|
|
|
sk->sk_state_change(sk);
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
nfc_llcp_sock_put(llcp_sock);
|
|
|
|
}
|
|
|
|
|
2021-07-30 22:41:59 +08:00
|
|
|
static void nfc_llcp_recv_dm(struct nfc_llcp_local *local,
|
|
|
|
const struct sk_buff *skb)
|
2012-06-26 22:13:29 +08:00
|
|
|
{
|
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
|
|
|
struct sock *sk;
|
|
|
|
u8 dsap, ssap, reason;
|
|
|
|
|
|
|
|
dsap = nfc_llcp_dsap(skb);
|
|
|
|
ssap = nfc_llcp_ssap(skb);
|
|
|
|
reason = skb->data[2];
|
|
|
|
|
|
|
|
pr_debug("%d %d reason %d\n", ssap, dsap, reason);
|
|
|
|
|
|
|
|
switch (reason) {
|
|
|
|
case LLCP_DM_NOBOUND:
|
|
|
|
case LLCP_DM_REJ:
|
|
|
|
llcp_sock = nfc_llcp_connecting_sock_get(local, dsap);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
llcp_sock = nfc_llcp_sock_get(local, dsap, ssap);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (llcp_sock == NULL) {
|
2012-10-17 03:15:59 +08:00
|
|
|
pr_debug("Already closed\n");
|
2012-06-26 22:13:29 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sk = &llcp_sock->sk;
|
|
|
|
|
|
|
|
sk->sk_err = ENXIO;
|
|
|
|
sk->sk_state = LLCP_CLOSED;
|
|
|
|
sk->sk_state_change(sk);
|
|
|
|
|
|
|
|
nfc_llcp_sock_put(llcp_sock);
|
|
|
|
}
|
|
|
|
|
2012-10-05 07:21:47 +08:00
|
|
|
static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
|
2021-07-30 22:41:59 +08:00
|
|
|
const struct sk_buff *skb)
|
2012-10-05 07:21:47 +08:00
|
|
|
{
|
|
|
|
struct nfc_llcp_sock *llcp_sock;
|
2021-07-30 22:41:59 +08:00
|
|
|
u8 dsap, ssap, type, length, tid, sap;
|
|
|
|
const u8 *tlv;
|
2012-10-05 07:21:47 +08:00
|
|
|
u16 tlv_len, offset;
|
2021-07-30 22:41:59 +08:00
|
|
|
const char *service_name;
|
2012-10-05 07:21:47 +08:00
|
|
|
size_t service_name_len;
|
2013-02-15 17:43:05 +08:00
|
|
|
struct nfc_llcp_sdp_tlv *sdp;
|
|
|
|
HLIST_HEAD(llc_sdres_list);
|
|
|
|
size_t sdres_tlvs_len;
|
2013-02-15 17:43:06 +08:00
|
|
|
HLIST_HEAD(nl_sdres_list);
|
2012-10-05 07:21:47 +08:00
|
|
|
|
|
|
|
dsap = nfc_llcp_dsap(skb);
|
|
|
|
ssap = nfc_llcp_ssap(skb);
|
|
|
|
|
|
|
|
pr_debug("%d %d\n", dsap, ssap);
|
|
|
|
|
|
|
|
if (dsap != LLCP_SAP_SDP || ssap != LLCP_SAP_SDP) {
|
|
|
|
pr_err("Wrong SNL SAP\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tlv = &skb->data[LLCP_HEADER_SIZE];
|
|
|
|
tlv_len = skb->len - LLCP_HEADER_SIZE;
|
|
|
|
offset = 0;
|
2013-02-15 17:43:05 +08:00
|
|
|
sdres_tlvs_len = 0;
|
2012-10-05 07:21:47 +08:00
|
|
|
|
2012-10-17 21:23:39 +08:00
|
|
|
while (offset < tlv_len) {
|
2012-10-05 07:21:47 +08:00
|
|
|
type = tlv[0];
|
|
|
|
length = tlv[1];
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case LLCP_TLV_SDREQ:
|
|
|
|
tid = tlv[2];
|
|
|
|
service_name = (char *) &tlv[3];
|
|
|
|
service_name_len = length - 1;
|
|
|
|
|
2012-10-15 20:28:13 +08:00
|
|
|
pr_debug("Looking for %.16s\n", service_name);
|
2012-10-05 07:21:47 +08:00
|
|
|
|
|
|
|
if (service_name_len == strlen("urn:nfc:sn:sdp") &&
|
|
|
|
!strncmp(service_name, "urn:nfc:sn:sdp",
|
|
|
|
service_name_len)) {
|
|
|
|
sap = 1;
|
2013-02-15 17:43:05 +08:00
|
|
|
goto add_snl;
|
2012-10-15 20:28:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
llcp_sock = nfc_llcp_sock_from_sn(local, service_name,
|
2023-10-09 20:31:10 +08:00
|
|
|
service_name_len,
|
|
|
|
true);
|
2012-10-15 20:28:13 +08:00
|
|
|
if (!llcp_sock) {
|
|
|
|
sap = 0;
|
2013-02-15 17:43:05 +08:00
|
|
|
goto add_snl;
|
2012-10-15 20:28:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We found a socket but its ssap has not been reserved
|
|
|
|
* yet. We need to assign it for good and send a reply.
|
|
|
|
* The ssap will be freed when the socket is closed.
|
|
|
|
*/
|
|
|
|
if (llcp_sock->ssap == LLCP_SDP_UNBOUND) {
|
|
|
|
atomic_t *client_count;
|
|
|
|
|
|
|
|
sap = nfc_llcp_reserve_sdp_ssap(local);
|
|
|
|
|
|
|
|
pr_debug("Reserving %d\n", sap);
|
|
|
|
|
|
|
|
if (sap == LLCP_SAP_MAX) {
|
|
|
|
sap = 0;
|
2023-10-09 20:31:10 +08:00
|
|
|
nfc_llcp_sock_put(llcp_sock);
|
2013-02-15 17:43:05 +08:00
|
|
|
goto add_snl;
|
2012-10-15 20:28:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
client_count =
|
|
|
|
&local->local_sdp_cnt[sap -
|
|
|
|
LLCP_WKS_NUM_SAP];
|
|
|
|
|
|
|
|
atomic_inc(client_count);
|
|
|
|
|
|
|
|
llcp_sock->ssap = sap;
|
|
|
|
llcp_sock->reserved_ssap = sap;
|
2012-10-05 07:21:47 +08:00
|
|
|
} else {
|
2012-10-15 20:28:13 +08:00
|
|
|
sap = llcp_sock->ssap;
|
2012-10-05 07:21:47 +08:00
|
|
|
}
|
|
|
|
|
2012-10-15 20:28:13 +08:00
|
|
|
pr_debug("%p %d\n", llcp_sock, sap);
|
|
|
|
|
2023-10-09 20:31:10 +08:00
|
|
|
nfc_llcp_sock_put(llcp_sock);
|
2013-02-15 17:43:05 +08:00
|
|
|
add_snl:
|
|
|
|
sdp = nfc_llcp_build_sdres_tlv(tid, sap);
|
|
|
|
if (sdp == NULL)
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
sdres_tlvs_len += sdp->tlv_len;
|
|
|
|
hlist_add_head(&sdp->node, &llc_sdres_list);
|
2012-10-05 07:21:47 +08:00
|
|
|
break;
|
|
|
|
|
2013-02-15 17:43:06 +08:00
|
|
|
case LLCP_TLV_SDRES:
|
|
|
|
mutex_lock(&local->sdreq_lock);
|
|
|
|
|
|
|
|
pr_debug("LLCP_TLV_SDRES: searching tid %d\n", tlv[2]);
|
|
|
|
|
|
|
|
hlist_for_each_entry(sdp, &local->pending_sdreqs, node) {
|
|
|
|
if (sdp->tid != tlv[2])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sdp->sap = tlv[3];
|
|
|
|
|
|
|
|
pr_debug("Found: uri=%s, sap=%d\n",
|
|
|
|
sdp->uri, sdp->sap);
|
|
|
|
|
|
|
|
hlist_del(&sdp->node);
|
|
|
|
|
|
|
|
hlist_add_head(&sdp->node, &nl_sdres_list);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&local->sdreq_lock);
|
|
|
|
break;
|
|
|
|
|
2012-10-05 07:21:47 +08:00
|
|
|
default:
|
|
|
|
pr_err("Invalid SNL tlv value 0x%x\n", type);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += length + 2;
|
|
|
|
tlv += length + 2;
|
|
|
|
}
|
2013-02-15 17:43:05 +08:00
|
|
|
|
|
|
|
exit:
|
2013-02-15 17:43:06 +08:00
|
|
|
if (!hlist_empty(&nl_sdres_list))
|
|
|
|
nfc_genl_llc_send_sdres(local->dev, &nl_sdres_list);
|
|
|
|
|
2013-02-15 17:43:05 +08:00
|
|
|
if (!hlist_empty(&llc_sdres_list))
|
|
|
|
nfc_llcp_send_snl_sdres(local, &llc_sdres_list, sdres_tlvs_len);
|
2012-10-05 07:21:47 +08:00
|
|
|
}
|
|
|
|
|
2013-03-29 18:47:43 +08:00
|
|
|
static void nfc_llcp_recv_agf(struct nfc_llcp_local *local, struct sk_buff *skb)
|
2011-12-14 23:43:12 +08:00
|
|
|
{
|
2013-03-29 18:47:43 +08:00
|
|
|
u8 ptype;
|
|
|
|
u16 pdu_len;
|
|
|
|
struct sk_buff *new_skb;
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2013-03-29 18:47:43 +08:00
|
|
|
if (skb->len <= LLCP_HEADER_SIZE) {
|
|
|
|
pr_err("Malformed AGF PDU\n");
|
2011-12-14 23:43:12 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-03-29 18:47:43 +08:00
|
|
|
skb_pull(skb, LLCP_HEADER_SIZE);
|
|
|
|
|
|
|
|
while (skb->len > LLCP_AGF_PDU_HEADER_SIZE) {
|
|
|
|
pdu_len = skb->data[0] << 8 | skb->data[1];
|
|
|
|
|
|
|
|
skb_pull(skb, LLCP_AGF_PDU_HEADER_SIZE);
|
|
|
|
|
|
|
|
if (pdu_len < LLCP_HEADER_SIZE || pdu_len > skb->len) {
|
|
|
|
pr_err("Malformed AGF PDU\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptype = nfc_llcp_ptype(skb);
|
|
|
|
|
|
|
|
if (ptype == LLCP_PDU_SYMM || ptype == LLCP_PDU_AGF)
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
new_skb = nfc_alloc_recv_skb(pdu_len, GFP_KERNEL);
|
|
|
|
if (new_skb == NULL) {
|
|
|
|
pr_err("Could not allocate PDU\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:20 +08:00
|
|
|
skb_put_data(new_skb, skb->data, pdu_len);
|
2013-03-29 18:47:43 +08:00
|
|
|
|
|
|
|
nfc_llcp_rx_skb(local, new_skb);
|
|
|
|
|
|
|
|
kfree_skb(new_skb);
|
|
|
|
next:
|
|
|
|
skb_pull(skb, pdu_len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfc_llcp_rx_skb(struct nfc_llcp_local *local, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
u8 dsap, ssap, ptype;
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
ptype = nfc_llcp_ptype(skb);
|
|
|
|
dsap = nfc_llcp_dsap(skb);
|
|
|
|
ssap = nfc_llcp_ssap(skb);
|
|
|
|
|
|
|
|
pr_debug("ptype 0x%x dsap 0x%x ssap 0x%x\n", ptype, dsap, ssap);
|
|
|
|
|
2012-04-11 01:43:13 +08:00
|
|
|
if (ptype != LLCP_PDU_SYMM)
|
2016-06-07 22:21:53 +08:00
|
|
|
print_hex_dump_debug("LLCP Rx: ", DUMP_PREFIX_OFFSET, 16, 1,
|
|
|
|
skb->data, skb->len, true);
|
2012-04-11 01:43:13 +08:00
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
switch (ptype) {
|
|
|
|
case LLCP_PDU_SYMM:
|
|
|
|
pr_debug("SYMM\n");
|
|
|
|
break;
|
|
|
|
|
2012-10-15 22:14:37 +08:00
|
|
|
case LLCP_PDU_UI:
|
|
|
|
pr_debug("UI\n");
|
|
|
|
nfc_llcp_recv_ui(local, skb);
|
|
|
|
break;
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
case LLCP_PDU_CONNECT:
|
|
|
|
pr_debug("CONNECT\n");
|
|
|
|
nfc_llcp_recv_connect(local, skb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LLCP_PDU_DISC:
|
|
|
|
pr_debug("DISC\n");
|
|
|
|
nfc_llcp_recv_disc(local, skb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LLCP_PDU_CC:
|
|
|
|
pr_debug("CC\n");
|
|
|
|
nfc_llcp_recv_cc(local, skb);
|
|
|
|
break;
|
|
|
|
|
2012-06-26 22:13:29 +08:00
|
|
|
case LLCP_PDU_DM:
|
|
|
|
pr_debug("DM\n");
|
|
|
|
nfc_llcp_recv_dm(local, skb);
|
|
|
|
break;
|
|
|
|
|
2012-10-05 07:21:47 +08:00
|
|
|
case LLCP_PDU_SNL:
|
|
|
|
pr_debug("SNL\n");
|
|
|
|
nfc_llcp_recv_snl(local, skb);
|
|
|
|
break;
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
case LLCP_PDU_I:
|
|
|
|
case LLCP_PDU_RR:
|
2012-03-05 08:03:36 +08:00
|
|
|
case LLCP_PDU_RNR:
|
2011-12-14 23:43:12 +08:00
|
|
|
pr_debug("I frame\n");
|
|
|
|
nfc_llcp_recv_hdlc(local, skb);
|
|
|
|
break;
|
|
|
|
|
2013-03-29 18:47:43 +08:00
|
|
|
case LLCP_PDU_AGF:
|
|
|
|
pr_debug("AGF frame\n");
|
|
|
|
nfc_llcp_recv_agf(local, skb);
|
|
|
|
break;
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
2013-03-29 18:47:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void nfc_llcp_rx_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
|
|
|
|
rx_work);
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
|
|
skb = local->rx_pending;
|
|
|
|
if (skb == NULL) {
|
|
|
|
pr_debug("No pending SKB\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
__net_timestamp(skb);
|
|
|
|
|
2014-05-05 18:43:31 +08:00
|
|
|
nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_RX);
|
2013-03-29 18:47:43 +08:00
|
|
|
|
|
|
|
nfc_llcp_rx_skb(local, skb);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
2012-10-03 07:01:31 +08:00
|
|
|
schedule_work(&local->tx_work);
|
2011-12-14 23:43:12 +08:00
|
|
|
kfree_skb(local->rx_pending);
|
|
|
|
local->rx_pending = NULL;
|
|
|
|
}
|
|
|
|
|
2012-12-07 23:37:30 +08:00
|
|
|
static void __nfc_llcp_recv(struct nfc_llcp_local *local, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
local->rx_pending = skb;
|
|
|
|
del_timer(&local->link_timer);
|
|
|
|
schedule_work(&local->rx_work);
|
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local = (struct nfc_llcp_local *) data;
|
|
|
|
|
|
|
|
if (err < 0) {
|
2021-10-11 21:38:29 +08:00
|
|
|
pr_err("LLCP PDU receive err %d\n", err);
|
2011-12-14 23:43:12 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-12-07 23:37:30 +08:00
|
|
|
__nfc_llcp_recv(local, skb);
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
2012-05-31 06:05:50 +08:00
|
|
|
int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local;
|
|
|
|
|
|
|
|
local = nfc_llcp_find_local(dev);
|
2014-10-21 22:52:50 +08:00
|
|
|
if (local == NULL) {
|
|
|
|
kfree_skb(skb);
|
2012-05-31 06:05:50 +08:00
|
|
|
return -ENODEV;
|
2014-10-21 22:52:50 +08:00
|
|
|
}
|
2012-05-31 06:05:50 +08:00
|
|
|
|
2012-12-07 23:37:30 +08:00
|
|
|
__nfc_llcp_recv(local, skb);
|
2012-05-31 06:05:50 +08:00
|
|
|
|
net: nfc: Fix use-after-free caused by nfc_llcp_find_local
This commit fixes several use-after-free that caused by function
nfc_llcp_find_local(). For example, one UAF can happen when below buggy
time window occurs.
// nfc_genl_llc_get_params | // nfc_unregister_device
|
dev = nfc_get_device(idx); | device_lock(...)
if (!dev) | dev->shutting_down = true;
return -ENODEV; | device_unlock(...);
|
device_lock(...); | // nfc_llcp_unregister_device
| nfc_llcp_find_local()
nfc_llcp_find_local(...); |
| local_cleanup()
if (!local) { |
rc = -ENODEV; | // nfc_llcp_local_put
goto exit; | kref_put(.., local_release)
} |
| // local_release
| list_del(&local->list)
// nfc_genl_send_params | kfree()
local->dev->idx !!!UAF!!! |
|
and the crash trace for the one of the discussed UAF like:
BUG: KASAN: slab-use-after-free in nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
Read of size 8 at addr ffff888105b0e410 by task 20114
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:319 [inline]
print_report+0xcc/0x620 mm/kasan/report.c:430
kasan_report+0xb2/0xe0 mm/kasan/report.c:536
nfc_genl_send_params net/nfc/netlink.c:999 [inline]
nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
genl_family_rcv_msg_doit.isra.0+0x1ee/0x2e0 net/netlink/genetlink.c:968
genl_family_rcv_msg net/netlink/genetlink.c:1048 [inline]
genl_rcv_msg+0x503/0x7d0 net/netlink/genetlink.c:1065
netlink_rcv_skb+0x161/0x430 net/netlink/af_netlink.c:2548
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1076
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x644/0x900 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x934/0xe70 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg+0x1b6/0x200 net/socket.c:747
____sys_sendmsg+0x6e9/0x890 net/socket.c:2501
___sys_sendmsg+0x110/0x1b0 net/socket.c:2555
__sys_sendmsg+0xf7/0x1d0 net/socket.c:2584
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
RIP: 0033:0x7f34640a2389
RSP: 002b:00007f3463415168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f34641c1f80 RCX: 00007f34640a2389
RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000006
RBP: 00007f34640ed493 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffe38449ecf R14: 00007f3463415300 R15: 0000000000022000
</TASK>
Allocated by task 20116:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
____kasan_kmalloc mm/kasan/common.c:374 [inline]
__kasan_kmalloc+0x7f/0x90 mm/kasan/common.c:383
kmalloc include/linux/slab.h:580 [inline]
kzalloc include/linux/slab.h:720 [inline]
nfc_llcp_register_device+0x49/0xa40 net/nfc/llcp_core.c:1567
nfc_register_device+0x61/0x260 net/nfc/core.c:1124
nci_register_device+0x776/0xb20 net/nfc/nci/core.c:1257
virtual_ncidev_open+0x147/0x230 drivers/nfc/virtual_ncidev.c:148
misc_open+0x379/0x4a0 drivers/char/misc.c:165
chrdev_open+0x26c/0x780 fs/char_dev.c:414
do_dentry_open+0x6c4/0x12a0 fs/open.c:920
do_open fs/namei.c:3560 [inline]
path_openat+0x24fe/0x37e0 fs/namei.c:3715
do_filp_open+0x1ba/0x410 fs/namei.c:3742
do_sys_openat2+0x171/0x4c0 fs/open.c:1356
do_sys_open fs/open.c:1372 [inline]
__do_sys_openat fs/open.c:1388 [inline]
__se_sys_openat fs/open.c:1383 [inline]
__x64_sys_openat+0x143/0x200 fs/open.c:1383
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Freed by task 20115:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521
____kasan_slab_free mm/kasan/common.c:236 [inline]
____kasan_slab_free mm/kasan/common.c:200 [inline]
__kasan_slab_free+0x10a/0x190 mm/kasan/common.c:244
kasan_slab_free include/linux/kasan.h:162 [inline]
slab_free_hook mm/slub.c:1781 [inline]
slab_free_freelist_hook mm/slub.c:1807 [inline]
slab_free mm/slub.c:3787 [inline]
__kmem_cache_free+0x7a/0x190 mm/slub.c:3800
local_release net/nfc/llcp_core.c:174 [inline]
kref_put include/linux/kref.h:65 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:182 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:177 [inline]
nfc_llcp_unregister_device+0x206/0x290 net/nfc/llcp_core.c:1620
nfc_unregister_device+0x160/0x1d0 net/nfc/core.c:1179
virtual_ncidev_close+0x52/0xa0 drivers/nfc/virtual_ncidev.c:163
__fput+0x252/0xa20 fs/file_table.c:321
task_work_run+0x174/0x270 kernel/task_work.c:179
resume_user_mode_work include/linux/resume_user_mode.h:49 [inline]
exit_to_user_mode_loop kernel/entry/common.c:171 [inline]
exit_to_user_mode_prepare+0x108/0x110 kernel/entry/common.c:204
__syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline]
syscall_exit_to_user_mode+0x21/0x50 kernel/entry/common.c:297
do_syscall_64+0x4c/0x90 arch/x86/entry/common.c:86
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Last potentially related work creation:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
__kasan_record_aux_stack+0x95/0xb0 mm/kasan/generic.c:491
kvfree_call_rcu+0x29/0xa80 kernel/rcu/tree.c:3328
drop_sysctl_table+0x3be/0x4e0 fs/proc/proc_sysctl.c:1735
unregister_sysctl_table.part.0+0x9c/0x190 fs/proc/proc_sysctl.c:1773
unregister_sysctl_table+0x24/0x30 fs/proc/proc_sysctl.c:1753
neigh_sysctl_unregister+0x5f/0x80 net/core/neighbour.c:3895
addrconf_notify+0x140/0x17b0 net/ipv6/addrconf.c:3684
notifier_call_chain+0xbe/0x210 kernel/notifier.c:87
call_netdevice_notifiers_info+0xb5/0x150 net/core/dev.c:1937
call_netdevice_notifiers_extack net/core/dev.c:1975 [inline]
call_netdevice_notifiers net/core/dev.c:1989 [inline]
dev_change_name+0x3c3/0x870 net/core/dev.c:1211
dev_ifsioc+0x800/0xf70 net/core/dev_ioctl.c:376
dev_ioctl+0x3d9/0xf80 net/core/dev_ioctl.c:542
sock_do_ioctl+0x160/0x260 net/socket.c:1213
sock_ioctl+0x3f9/0x670 net/socket.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:870 [inline]
__se_sys_ioctl fs/ioctl.c:856 [inline]
__x64_sys_ioctl+0x19e/0x210 fs/ioctl.c:856
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
The buggy address belongs to the object at ffff888105b0e400
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 16 bytes inside of
freed 1024-byte region [ffff888105b0e400, ffff888105b0e800)
The buggy address belongs to the physical page:
head:ffffea000416c200 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000010200(slab|head|node=0|zone=2)
raw: 0200000000010200 ffff8881000430c0 ffffea00044c7010 ffffea0004510e10
raw: 0000000000000000 00000000000a000a 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888105b0e300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888105b0e380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888105b0e400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888105b0e480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888105b0e500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
In summary, this patch solves those use-after-free by
1. Re-implement the nfc_llcp_find_local(). The current version does not
grab the reference when getting the local from the linked list. For
example, the llcp_sock_bind() gets the reference like below:
// llcp_sock_bind()
local = nfc_llcp_find_local(dev); // A
..... \
| raceable
..... /
llcp_sock->local = nfc_llcp_local_get(local); // B
There is an apparent race window that one can drop the reference
and free the local object fetched in (A) before (B) gets the reference.
2. Some callers of the nfc_llcp_find_local() do not grab the reference
at all. For example, the nfc_genl_llc_{{get/set}_params/sdreq} functions.
We add the nfc_llcp_local_put() for them. Moreover, we add the necessary
error handling function to put the reference.
3. Add the nfc_llcp_remove_local() helper. The local object is removed
from the linked list in local_release() when all reference is gone. This
patch removes it when nfc_llcp_unregister_device() is called.
Therefore, every caller of nfc_llcp_find_local() will get a reference
even when the nfc_llcp_unregister_device() is called. This promises no
use-after-free for the local object is ever possible.
Fixes: 52feb444a903 ("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support")
Fixes: c7aa12252f51 ("NFC: Take a reference on the LLCP local pointer when creating a socket")
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-06-25 17:10:07 +08:00
|
|
|
nfc_llcp_local_put(local);
|
|
|
|
|
2012-05-31 06:05:50 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
void nfc_llcp_mac_is_down(struct nfc_dev *dev)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local;
|
|
|
|
|
|
|
|
local = nfc_llcp_find_local(dev);
|
|
|
|
if (local == NULL)
|
|
|
|
return;
|
|
|
|
|
2013-04-02 16:25:15 +08:00
|
|
|
local->remote_miu = LLCP_DEFAULT_MIU;
|
|
|
|
local->remote_lto = LLCP_DEFAULT_LTO;
|
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
/* Close and purge all existing sockets */
|
2013-02-22 00:01:06 +08:00
|
|
|
nfc_llcp_socket_release(local, true, 0);
|
net: nfc: Fix use-after-free caused by nfc_llcp_find_local
This commit fixes several use-after-free that caused by function
nfc_llcp_find_local(). For example, one UAF can happen when below buggy
time window occurs.
// nfc_genl_llc_get_params | // nfc_unregister_device
|
dev = nfc_get_device(idx); | device_lock(...)
if (!dev) | dev->shutting_down = true;
return -ENODEV; | device_unlock(...);
|
device_lock(...); | // nfc_llcp_unregister_device
| nfc_llcp_find_local()
nfc_llcp_find_local(...); |
| local_cleanup()
if (!local) { |
rc = -ENODEV; | // nfc_llcp_local_put
goto exit; | kref_put(.., local_release)
} |
| // local_release
| list_del(&local->list)
// nfc_genl_send_params | kfree()
local->dev->idx !!!UAF!!! |
|
and the crash trace for the one of the discussed UAF like:
BUG: KASAN: slab-use-after-free in nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
Read of size 8 at addr ffff888105b0e410 by task 20114
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:319 [inline]
print_report+0xcc/0x620 mm/kasan/report.c:430
kasan_report+0xb2/0xe0 mm/kasan/report.c:536
nfc_genl_send_params net/nfc/netlink.c:999 [inline]
nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
genl_family_rcv_msg_doit.isra.0+0x1ee/0x2e0 net/netlink/genetlink.c:968
genl_family_rcv_msg net/netlink/genetlink.c:1048 [inline]
genl_rcv_msg+0x503/0x7d0 net/netlink/genetlink.c:1065
netlink_rcv_skb+0x161/0x430 net/netlink/af_netlink.c:2548
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1076
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x644/0x900 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x934/0xe70 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg+0x1b6/0x200 net/socket.c:747
____sys_sendmsg+0x6e9/0x890 net/socket.c:2501
___sys_sendmsg+0x110/0x1b0 net/socket.c:2555
__sys_sendmsg+0xf7/0x1d0 net/socket.c:2584
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
RIP: 0033:0x7f34640a2389
RSP: 002b:00007f3463415168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f34641c1f80 RCX: 00007f34640a2389
RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000006
RBP: 00007f34640ed493 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffe38449ecf R14: 00007f3463415300 R15: 0000000000022000
</TASK>
Allocated by task 20116:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
____kasan_kmalloc mm/kasan/common.c:374 [inline]
__kasan_kmalloc+0x7f/0x90 mm/kasan/common.c:383
kmalloc include/linux/slab.h:580 [inline]
kzalloc include/linux/slab.h:720 [inline]
nfc_llcp_register_device+0x49/0xa40 net/nfc/llcp_core.c:1567
nfc_register_device+0x61/0x260 net/nfc/core.c:1124
nci_register_device+0x776/0xb20 net/nfc/nci/core.c:1257
virtual_ncidev_open+0x147/0x230 drivers/nfc/virtual_ncidev.c:148
misc_open+0x379/0x4a0 drivers/char/misc.c:165
chrdev_open+0x26c/0x780 fs/char_dev.c:414
do_dentry_open+0x6c4/0x12a0 fs/open.c:920
do_open fs/namei.c:3560 [inline]
path_openat+0x24fe/0x37e0 fs/namei.c:3715
do_filp_open+0x1ba/0x410 fs/namei.c:3742
do_sys_openat2+0x171/0x4c0 fs/open.c:1356
do_sys_open fs/open.c:1372 [inline]
__do_sys_openat fs/open.c:1388 [inline]
__se_sys_openat fs/open.c:1383 [inline]
__x64_sys_openat+0x143/0x200 fs/open.c:1383
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Freed by task 20115:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521
____kasan_slab_free mm/kasan/common.c:236 [inline]
____kasan_slab_free mm/kasan/common.c:200 [inline]
__kasan_slab_free+0x10a/0x190 mm/kasan/common.c:244
kasan_slab_free include/linux/kasan.h:162 [inline]
slab_free_hook mm/slub.c:1781 [inline]
slab_free_freelist_hook mm/slub.c:1807 [inline]
slab_free mm/slub.c:3787 [inline]
__kmem_cache_free+0x7a/0x190 mm/slub.c:3800
local_release net/nfc/llcp_core.c:174 [inline]
kref_put include/linux/kref.h:65 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:182 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:177 [inline]
nfc_llcp_unregister_device+0x206/0x290 net/nfc/llcp_core.c:1620
nfc_unregister_device+0x160/0x1d0 net/nfc/core.c:1179
virtual_ncidev_close+0x52/0xa0 drivers/nfc/virtual_ncidev.c:163
__fput+0x252/0xa20 fs/file_table.c:321
task_work_run+0x174/0x270 kernel/task_work.c:179
resume_user_mode_work include/linux/resume_user_mode.h:49 [inline]
exit_to_user_mode_loop kernel/entry/common.c:171 [inline]
exit_to_user_mode_prepare+0x108/0x110 kernel/entry/common.c:204
__syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline]
syscall_exit_to_user_mode+0x21/0x50 kernel/entry/common.c:297
do_syscall_64+0x4c/0x90 arch/x86/entry/common.c:86
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Last potentially related work creation:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
__kasan_record_aux_stack+0x95/0xb0 mm/kasan/generic.c:491
kvfree_call_rcu+0x29/0xa80 kernel/rcu/tree.c:3328
drop_sysctl_table+0x3be/0x4e0 fs/proc/proc_sysctl.c:1735
unregister_sysctl_table.part.0+0x9c/0x190 fs/proc/proc_sysctl.c:1773
unregister_sysctl_table+0x24/0x30 fs/proc/proc_sysctl.c:1753
neigh_sysctl_unregister+0x5f/0x80 net/core/neighbour.c:3895
addrconf_notify+0x140/0x17b0 net/ipv6/addrconf.c:3684
notifier_call_chain+0xbe/0x210 kernel/notifier.c:87
call_netdevice_notifiers_info+0xb5/0x150 net/core/dev.c:1937
call_netdevice_notifiers_extack net/core/dev.c:1975 [inline]
call_netdevice_notifiers net/core/dev.c:1989 [inline]
dev_change_name+0x3c3/0x870 net/core/dev.c:1211
dev_ifsioc+0x800/0xf70 net/core/dev_ioctl.c:376
dev_ioctl+0x3d9/0xf80 net/core/dev_ioctl.c:542
sock_do_ioctl+0x160/0x260 net/socket.c:1213
sock_ioctl+0x3f9/0x670 net/socket.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:870 [inline]
__se_sys_ioctl fs/ioctl.c:856 [inline]
__x64_sys_ioctl+0x19e/0x210 fs/ioctl.c:856
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
The buggy address belongs to the object at ffff888105b0e400
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 16 bytes inside of
freed 1024-byte region [ffff888105b0e400, ffff888105b0e800)
The buggy address belongs to the physical page:
head:ffffea000416c200 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000010200(slab|head|node=0|zone=2)
raw: 0200000000010200 ffff8881000430c0 ffffea00044c7010 ffffea0004510e10
raw: 0000000000000000 00000000000a000a 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888105b0e300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888105b0e380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888105b0e400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888105b0e480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888105b0e500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
In summary, this patch solves those use-after-free by
1. Re-implement the nfc_llcp_find_local(). The current version does not
grab the reference when getting the local from the linked list. For
example, the llcp_sock_bind() gets the reference like below:
// llcp_sock_bind()
local = nfc_llcp_find_local(dev); // A
..... \
| raceable
..... /
llcp_sock->local = nfc_llcp_local_get(local); // B
There is an apparent race window that one can drop the reference
and free the local object fetched in (A) before (B) gets the reference.
2. Some callers of the nfc_llcp_find_local() do not grab the reference
at all. For example, the nfc_genl_llc_{{get/set}_params/sdreq} functions.
We add the nfc_llcp_local_put() for them. Moreover, we add the necessary
error handling function to put the reference.
3. Add the nfc_llcp_remove_local() helper. The local object is removed
from the linked list in local_release() when all reference is gone. This
patch removes it when nfc_llcp_unregister_device() is called.
Therefore, every caller of nfc_llcp_find_local() will get a reference
even when the nfc_llcp_unregister_device() is called. This promises no
use-after-free for the local object is ever possible.
Fixes: 52feb444a903 ("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support")
Fixes: c7aa12252f51 ("NFC: Take a reference on the LLCP local pointer when creating a socket")
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-06-25 17:10:07 +08:00
|
|
|
|
|
|
|
nfc_llcp_local_put(local);
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx,
|
|
|
|
u8 comm_mode, u8 rf_mode)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local;
|
|
|
|
|
|
|
|
pr_debug("rf mode %d\n", rf_mode);
|
|
|
|
|
|
|
|
local = nfc_llcp_find_local(dev);
|
|
|
|
if (local == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
local->target_idx = target_idx;
|
|
|
|
local->comm_mode = comm_mode;
|
|
|
|
local->rf_mode = rf_mode;
|
|
|
|
|
|
|
|
if (rf_mode == NFC_RF_INITIATOR) {
|
|
|
|
pr_debug("Queueing Tx work\n");
|
|
|
|
|
2012-10-03 07:01:31 +08:00
|
|
|
schedule_work(&local->tx_work);
|
2011-12-14 23:43:12 +08:00
|
|
|
} else {
|
|
|
|
mod_timer(&local->link_timer,
|
2012-03-05 08:03:52 +08:00
|
|
|
jiffies + msecs_to_jiffies(local->remote_lto));
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
net: nfc: Fix use-after-free caused by nfc_llcp_find_local
This commit fixes several use-after-free that caused by function
nfc_llcp_find_local(). For example, one UAF can happen when below buggy
time window occurs.
// nfc_genl_llc_get_params | // nfc_unregister_device
|
dev = nfc_get_device(idx); | device_lock(...)
if (!dev) | dev->shutting_down = true;
return -ENODEV; | device_unlock(...);
|
device_lock(...); | // nfc_llcp_unregister_device
| nfc_llcp_find_local()
nfc_llcp_find_local(...); |
| local_cleanup()
if (!local) { |
rc = -ENODEV; | // nfc_llcp_local_put
goto exit; | kref_put(.., local_release)
} |
| // local_release
| list_del(&local->list)
// nfc_genl_send_params | kfree()
local->dev->idx !!!UAF!!! |
|
and the crash trace for the one of the discussed UAF like:
BUG: KASAN: slab-use-after-free in nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
Read of size 8 at addr ffff888105b0e410 by task 20114
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:319 [inline]
print_report+0xcc/0x620 mm/kasan/report.c:430
kasan_report+0xb2/0xe0 mm/kasan/report.c:536
nfc_genl_send_params net/nfc/netlink.c:999 [inline]
nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
genl_family_rcv_msg_doit.isra.0+0x1ee/0x2e0 net/netlink/genetlink.c:968
genl_family_rcv_msg net/netlink/genetlink.c:1048 [inline]
genl_rcv_msg+0x503/0x7d0 net/netlink/genetlink.c:1065
netlink_rcv_skb+0x161/0x430 net/netlink/af_netlink.c:2548
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1076
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x644/0x900 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x934/0xe70 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg+0x1b6/0x200 net/socket.c:747
____sys_sendmsg+0x6e9/0x890 net/socket.c:2501
___sys_sendmsg+0x110/0x1b0 net/socket.c:2555
__sys_sendmsg+0xf7/0x1d0 net/socket.c:2584
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
RIP: 0033:0x7f34640a2389
RSP: 002b:00007f3463415168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f34641c1f80 RCX: 00007f34640a2389
RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000006
RBP: 00007f34640ed493 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffe38449ecf R14: 00007f3463415300 R15: 0000000000022000
</TASK>
Allocated by task 20116:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
____kasan_kmalloc mm/kasan/common.c:374 [inline]
__kasan_kmalloc+0x7f/0x90 mm/kasan/common.c:383
kmalloc include/linux/slab.h:580 [inline]
kzalloc include/linux/slab.h:720 [inline]
nfc_llcp_register_device+0x49/0xa40 net/nfc/llcp_core.c:1567
nfc_register_device+0x61/0x260 net/nfc/core.c:1124
nci_register_device+0x776/0xb20 net/nfc/nci/core.c:1257
virtual_ncidev_open+0x147/0x230 drivers/nfc/virtual_ncidev.c:148
misc_open+0x379/0x4a0 drivers/char/misc.c:165
chrdev_open+0x26c/0x780 fs/char_dev.c:414
do_dentry_open+0x6c4/0x12a0 fs/open.c:920
do_open fs/namei.c:3560 [inline]
path_openat+0x24fe/0x37e0 fs/namei.c:3715
do_filp_open+0x1ba/0x410 fs/namei.c:3742
do_sys_openat2+0x171/0x4c0 fs/open.c:1356
do_sys_open fs/open.c:1372 [inline]
__do_sys_openat fs/open.c:1388 [inline]
__se_sys_openat fs/open.c:1383 [inline]
__x64_sys_openat+0x143/0x200 fs/open.c:1383
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Freed by task 20115:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521
____kasan_slab_free mm/kasan/common.c:236 [inline]
____kasan_slab_free mm/kasan/common.c:200 [inline]
__kasan_slab_free+0x10a/0x190 mm/kasan/common.c:244
kasan_slab_free include/linux/kasan.h:162 [inline]
slab_free_hook mm/slub.c:1781 [inline]
slab_free_freelist_hook mm/slub.c:1807 [inline]
slab_free mm/slub.c:3787 [inline]
__kmem_cache_free+0x7a/0x190 mm/slub.c:3800
local_release net/nfc/llcp_core.c:174 [inline]
kref_put include/linux/kref.h:65 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:182 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:177 [inline]
nfc_llcp_unregister_device+0x206/0x290 net/nfc/llcp_core.c:1620
nfc_unregister_device+0x160/0x1d0 net/nfc/core.c:1179
virtual_ncidev_close+0x52/0xa0 drivers/nfc/virtual_ncidev.c:163
__fput+0x252/0xa20 fs/file_table.c:321
task_work_run+0x174/0x270 kernel/task_work.c:179
resume_user_mode_work include/linux/resume_user_mode.h:49 [inline]
exit_to_user_mode_loop kernel/entry/common.c:171 [inline]
exit_to_user_mode_prepare+0x108/0x110 kernel/entry/common.c:204
__syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline]
syscall_exit_to_user_mode+0x21/0x50 kernel/entry/common.c:297
do_syscall_64+0x4c/0x90 arch/x86/entry/common.c:86
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Last potentially related work creation:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
__kasan_record_aux_stack+0x95/0xb0 mm/kasan/generic.c:491
kvfree_call_rcu+0x29/0xa80 kernel/rcu/tree.c:3328
drop_sysctl_table+0x3be/0x4e0 fs/proc/proc_sysctl.c:1735
unregister_sysctl_table.part.0+0x9c/0x190 fs/proc/proc_sysctl.c:1773
unregister_sysctl_table+0x24/0x30 fs/proc/proc_sysctl.c:1753
neigh_sysctl_unregister+0x5f/0x80 net/core/neighbour.c:3895
addrconf_notify+0x140/0x17b0 net/ipv6/addrconf.c:3684
notifier_call_chain+0xbe/0x210 kernel/notifier.c:87
call_netdevice_notifiers_info+0xb5/0x150 net/core/dev.c:1937
call_netdevice_notifiers_extack net/core/dev.c:1975 [inline]
call_netdevice_notifiers net/core/dev.c:1989 [inline]
dev_change_name+0x3c3/0x870 net/core/dev.c:1211
dev_ifsioc+0x800/0xf70 net/core/dev_ioctl.c:376
dev_ioctl+0x3d9/0xf80 net/core/dev_ioctl.c:542
sock_do_ioctl+0x160/0x260 net/socket.c:1213
sock_ioctl+0x3f9/0x670 net/socket.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:870 [inline]
__se_sys_ioctl fs/ioctl.c:856 [inline]
__x64_sys_ioctl+0x19e/0x210 fs/ioctl.c:856
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
The buggy address belongs to the object at ffff888105b0e400
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 16 bytes inside of
freed 1024-byte region [ffff888105b0e400, ffff888105b0e800)
The buggy address belongs to the physical page:
head:ffffea000416c200 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000010200(slab|head|node=0|zone=2)
raw: 0200000000010200 ffff8881000430c0 ffffea00044c7010 ffffea0004510e10
raw: 0000000000000000 00000000000a000a 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888105b0e300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888105b0e380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888105b0e400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888105b0e480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888105b0e500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
In summary, this patch solves those use-after-free by
1. Re-implement the nfc_llcp_find_local(). The current version does not
grab the reference when getting the local from the linked list. For
example, the llcp_sock_bind() gets the reference like below:
// llcp_sock_bind()
local = nfc_llcp_find_local(dev); // A
..... \
| raceable
..... /
llcp_sock->local = nfc_llcp_local_get(local); // B
There is an apparent race window that one can drop the reference
and free the local object fetched in (A) before (B) gets the reference.
2. Some callers of the nfc_llcp_find_local() do not grab the reference
at all. For example, the nfc_genl_llc_{{get/set}_params/sdreq} functions.
We add the nfc_llcp_local_put() for them. Moreover, we add the necessary
error handling function to put the reference.
3. Add the nfc_llcp_remove_local() helper. The local object is removed
from the linked list in local_release() when all reference is gone. This
patch removes it when nfc_llcp_unregister_device() is called.
Therefore, every caller of nfc_llcp_find_local() will get a reference
even when the nfc_llcp_unregister_device() is called. This promises no
use-after-free for the local object is ever possible.
Fixes: 52feb444a903 ("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support")
Fixes: c7aa12252f51 ("NFC: Take a reference on the LLCP local pointer when creating a socket")
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-06-25 17:10:07 +08:00
|
|
|
|
|
|
|
nfc_llcp_local_put(local);
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int nfc_llcp_register_device(struct nfc_dev *ndev)
|
|
|
|
{
|
|
|
|
struct nfc_llcp_local *local;
|
|
|
|
|
|
|
|
local = kzalloc(sizeof(struct nfc_llcp_local), GFP_KERNEL);
|
|
|
|
if (local == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
local->dev = ndev;
|
|
|
|
INIT_LIST_HEAD(&local->list);
|
2012-05-04 17:24:16 +08:00
|
|
|
kref_init(&local->ref);
|
2011-12-14 23:43:12 +08:00
|
|
|
mutex_init(&local->sdp_lock);
|
2017-10-11 18:33:44 +08:00
|
|
|
timer_setup(&local->link_timer, nfc_llcp_symm_timer, 0);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
skb_queue_head_init(&local->tx_queue);
|
|
|
|
INIT_WORK(&local->tx_work, nfc_llcp_tx_work);
|
|
|
|
|
|
|
|
local->rx_pending = NULL;
|
|
|
|
INIT_WORK(&local->rx_work, nfc_llcp_rx_work);
|
|
|
|
|
|
|
|
INIT_WORK(&local->timeout_work, nfc_llcp_timeout_work);
|
|
|
|
|
2012-09-25 18:42:50 +08:00
|
|
|
rwlock_init(&local->sockets.lock);
|
|
|
|
rwlock_init(&local->connecting_sockets.lock);
|
2012-09-27 00:16:44 +08:00
|
|
|
rwlock_init(&local->raw_sockets.lock);
|
2012-05-04 23:04:19 +08:00
|
|
|
|
2012-10-17 20:43:39 +08:00
|
|
|
local->lto = 150; /* 1500 ms */
|
|
|
|
local->rw = LLCP_MAX_RW;
|
|
|
|
local->miux = cpu_to_be16(LLCP_MAX_MIUX);
|
2013-05-28 21:41:32 +08:00
|
|
|
local->local_wks = 0x1; /* LLC Link Management */
|
2012-10-17 20:43:39 +08:00
|
|
|
|
2011-12-14 23:43:12 +08:00
|
|
|
nfc_llcp_build_gb(local);
|
|
|
|
|
|
|
|
local->remote_miu = LLCP_DEFAULT_MIU;
|
|
|
|
local->remote_lto = LLCP_DEFAULT_LTO;
|
|
|
|
|
2013-02-15 17:43:06 +08:00
|
|
|
mutex_init(&local->sdreq_lock);
|
|
|
|
INIT_HLIST_HEAD(&local->pending_sdreqs);
|
2017-10-11 18:33:44 +08:00
|
|
|
timer_setup(&local->sdreq_timer, nfc_llcp_sdreq_timer, 0);
|
2013-03-04 22:43:32 +08:00
|
|
|
INIT_WORK(&local->sdreq_timeout_work, nfc_llcp_sdreq_timeout_work);
|
2013-02-15 17:43:06 +08:00
|
|
|
|
2023-09-09 07:58:53 +08:00
|
|
|
spin_lock(&llcp_devices_lock);
|
2012-10-12 21:25:43 +08:00
|
|
|
list_add(&local->list, &llcp_devices);
|
2023-09-09 07:58:53 +08:00
|
|
|
spin_unlock(&llcp_devices_lock);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfc_llcp_unregister_device(struct nfc_dev *dev)
|
|
|
|
{
|
net: nfc: Fix use-after-free caused by nfc_llcp_find_local
This commit fixes several use-after-free that caused by function
nfc_llcp_find_local(). For example, one UAF can happen when below buggy
time window occurs.
// nfc_genl_llc_get_params | // nfc_unregister_device
|
dev = nfc_get_device(idx); | device_lock(...)
if (!dev) | dev->shutting_down = true;
return -ENODEV; | device_unlock(...);
|
device_lock(...); | // nfc_llcp_unregister_device
| nfc_llcp_find_local()
nfc_llcp_find_local(...); |
| local_cleanup()
if (!local) { |
rc = -ENODEV; | // nfc_llcp_local_put
goto exit; | kref_put(.., local_release)
} |
| // local_release
| list_del(&local->list)
// nfc_genl_send_params | kfree()
local->dev->idx !!!UAF!!! |
|
and the crash trace for the one of the discussed UAF like:
BUG: KASAN: slab-use-after-free in nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
Read of size 8 at addr ffff888105b0e410 by task 20114
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x72/0xa0 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:319 [inline]
print_report+0xcc/0x620 mm/kasan/report.c:430
kasan_report+0xb2/0xe0 mm/kasan/report.c:536
nfc_genl_send_params net/nfc/netlink.c:999 [inline]
nfc_genl_llc_get_params+0x72f/0x780 net/nfc/netlink.c:1045
genl_family_rcv_msg_doit.isra.0+0x1ee/0x2e0 net/netlink/genetlink.c:968
genl_family_rcv_msg net/netlink/genetlink.c:1048 [inline]
genl_rcv_msg+0x503/0x7d0 net/netlink/genetlink.c:1065
netlink_rcv_skb+0x161/0x430 net/netlink/af_netlink.c:2548
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1076
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x644/0x900 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x934/0xe70 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg+0x1b6/0x200 net/socket.c:747
____sys_sendmsg+0x6e9/0x890 net/socket.c:2501
___sys_sendmsg+0x110/0x1b0 net/socket.c:2555
__sys_sendmsg+0xf7/0x1d0 net/socket.c:2584
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
RIP: 0033:0x7f34640a2389
RSP: 002b:00007f3463415168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f34641c1f80 RCX: 00007f34640a2389
RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000006
RBP: 00007f34640ed493 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffe38449ecf R14: 00007f3463415300 R15: 0000000000022000
</TASK>
Allocated by task 20116:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
____kasan_kmalloc mm/kasan/common.c:374 [inline]
__kasan_kmalloc+0x7f/0x90 mm/kasan/common.c:383
kmalloc include/linux/slab.h:580 [inline]
kzalloc include/linux/slab.h:720 [inline]
nfc_llcp_register_device+0x49/0xa40 net/nfc/llcp_core.c:1567
nfc_register_device+0x61/0x260 net/nfc/core.c:1124
nci_register_device+0x776/0xb20 net/nfc/nci/core.c:1257
virtual_ncidev_open+0x147/0x230 drivers/nfc/virtual_ncidev.c:148
misc_open+0x379/0x4a0 drivers/char/misc.c:165
chrdev_open+0x26c/0x780 fs/char_dev.c:414
do_dentry_open+0x6c4/0x12a0 fs/open.c:920
do_open fs/namei.c:3560 [inline]
path_openat+0x24fe/0x37e0 fs/namei.c:3715
do_filp_open+0x1ba/0x410 fs/namei.c:3742
do_sys_openat2+0x171/0x4c0 fs/open.c:1356
do_sys_open fs/open.c:1372 [inline]
__do_sys_openat fs/open.c:1388 [inline]
__se_sys_openat fs/open.c:1383 [inline]
__x64_sys_openat+0x143/0x200 fs/open.c:1383
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Freed by task 20115:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
kasan_set_track+0x25/0x30 mm/kasan/common.c:52
kasan_save_free_info+0x2e/0x50 mm/kasan/generic.c:521
____kasan_slab_free mm/kasan/common.c:236 [inline]
____kasan_slab_free mm/kasan/common.c:200 [inline]
__kasan_slab_free+0x10a/0x190 mm/kasan/common.c:244
kasan_slab_free include/linux/kasan.h:162 [inline]
slab_free_hook mm/slub.c:1781 [inline]
slab_free_freelist_hook mm/slub.c:1807 [inline]
slab_free mm/slub.c:3787 [inline]
__kmem_cache_free+0x7a/0x190 mm/slub.c:3800
local_release net/nfc/llcp_core.c:174 [inline]
kref_put include/linux/kref.h:65 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:182 [inline]
nfc_llcp_local_put net/nfc/llcp_core.c:177 [inline]
nfc_llcp_unregister_device+0x206/0x290 net/nfc/llcp_core.c:1620
nfc_unregister_device+0x160/0x1d0 net/nfc/core.c:1179
virtual_ncidev_close+0x52/0xa0 drivers/nfc/virtual_ncidev.c:163
__fput+0x252/0xa20 fs/file_table.c:321
task_work_run+0x174/0x270 kernel/task_work.c:179
resume_user_mode_work include/linux/resume_user_mode.h:49 [inline]
exit_to_user_mode_loop kernel/entry/common.c:171 [inline]
exit_to_user_mode_prepare+0x108/0x110 kernel/entry/common.c:204
__syscall_exit_to_user_mode_work kernel/entry/common.c:286 [inline]
syscall_exit_to_user_mode+0x21/0x50 kernel/entry/common.c:297
do_syscall_64+0x4c/0x90 arch/x86/entry/common.c:86
entry_SYSCALL_64_after_hwframe+0x72/0xdc
Last potentially related work creation:
kasan_save_stack+0x22/0x50 mm/kasan/common.c:45
__kasan_record_aux_stack+0x95/0xb0 mm/kasan/generic.c:491
kvfree_call_rcu+0x29/0xa80 kernel/rcu/tree.c:3328
drop_sysctl_table+0x3be/0x4e0 fs/proc/proc_sysctl.c:1735
unregister_sysctl_table.part.0+0x9c/0x190 fs/proc/proc_sysctl.c:1773
unregister_sysctl_table+0x24/0x30 fs/proc/proc_sysctl.c:1753
neigh_sysctl_unregister+0x5f/0x80 net/core/neighbour.c:3895
addrconf_notify+0x140/0x17b0 net/ipv6/addrconf.c:3684
notifier_call_chain+0xbe/0x210 kernel/notifier.c:87
call_netdevice_notifiers_info+0xb5/0x150 net/core/dev.c:1937
call_netdevice_notifiers_extack net/core/dev.c:1975 [inline]
call_netdevice_notifiers net/core/dev.c:1989 [inline]
dev_change_name+0x3c3/0x870 net/core/dev.c:1211
dev_ifsioc+0x800/0xf70 net/core/dev_ioctl.c:376
dev_ioctl+0x3d9/0xf80 net/core/dev_ioctl.c:542
sock_do_ioctl+0x160/0x260 net/socket.c:1213
sock_ioctl+0x3f9/0x670 net/socket.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:870 [inline]
__se_sys_ioctl fs/ioctl.c:856 [inline]
__x64_sys_ioctl+0x19e/0x210 fs/ioctl.c:856
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x3f/0x90 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x72/0xdc
The buggy address belongs to the object at ffff888105b0e400
which belongs to the cache kmalloc-1k of size 1024
The buggy address is located 16 bytes inside of
freed 1024-byte region [ffff888105b0e400, ffff888105b0e800)
The buggy address belongs to the physical page:
head:ffffea000416c200 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000010200(slab|head|node=0|zone=2)
raw: 0200000000010200 ffff8881000430c0 ffffea00044c7010 ffffea0004510e10
raw: 0000000000000000 00000000000a000a 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888105b0e300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888105b0e380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888105b0e400: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888105b0e480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888105b0e500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
In summary, this patch solves those use-after-free by
1. Re-implement the nfc_llcp_find_local(). The current version does not
grab the reference when getting the local from the linked list. For
example, the llcp_sock_bind() gets the reference like below:
// llcp_sock_bind()
local = nfc_llcp_find_local(dev); // A
..... \
| raceable
..... /
llcp_sock->local = nfc_llcp_local_get(local); // B
There is an apparent race window that one can drop the reference
and free the local object fetched in (A) before (B) gets the reference.
2. Some callers of the nfc_llcp_find_local() do not grab the reference
at all. For example, the nfc_genl_llc_{{get/set}_params/sdreq} functions.
We add the nfc_llcp_local_put() for them. Moreover, we add the necessary
error handling function to put the reference.
3. Add the nfc_llcp_remove_local() helper. The local object is removed
from the linked list in local_release() when all reference is gone. This
patch removes it when nfc_llcp_unregister_device() is called.
Therefore, every caller of nfc_llcp_find_local() will get a reference
even when the nfc_llcp_unregister_device() is called. This promises no
use-after-free for the local object is ever possible.
Fixes: 52feb444a903 ("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support")
Fixes: c7aa12252f51 ("NFC: Take a reference on the LLCP local pointer when creating a socket")
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2023-06-25 17:10:07 +08:00
|
|
|
struct nfc_llcp_local *local = nfc_llcp_remove_local(dev);
|
2011-12-14 23:43:12 +08:00
|
|
|
|
|
|
|
if (local == NULL) {
|
|
|
|
pr_debug("No such device\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-04-03 22:40:52 +08:00
|
|
|
local_cleanup(local);
|
2013-02-21 22:40:04 +08:00
|
|
|
|
2012-05-04 17:24:16 +08:00
|
|
|
nfc_llcp_local_put(local);
|
2011-12-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int __init nfc_llcp_init(void)
|
|
|
|
{
|
|
|
|
return nfc_llcp_sock_init();
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfc_llcp_exit(void)
|
|
|
|
{
|
|
|
|
nfc_llcp_sock_exit();
|
|
|
|
}
|