Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Lin Ma and committed by
David S. Miller
6709d4b7 d1b35543

+78 -25
-1
net/nfc/llcp.h
··· 201 201 void nfc_llcp_sock_unlink(struct llcp_sock_list *l, struct sock *s); 202 202 void nfc_llcp_socket_remote_param_init(struct nfc_llcp_sock *sock); 203 203 struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev); 204 - struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local); 205 204 int nfc_llcp_local_put(struct nfc_llcp_local *local); 206 205 u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local, 207 206 struct nfc_llcp_sock *sock);
+9 -3
net/nfc/llcp_commands.c
··· 359 359 struct sk_buff *skb; 360 360 struct nfc_llcp_local *local; 361 361 u16 size = 0; 362 + int err; 362 363 363 364 local = nfc_llcp_find_local(dev); 364 365 if (local == NULL) ··· 369 368 size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; 370 369 371 370 skb = alloc_skb(size, GFP_KERNEL); 372 - if (skb == NULL) 373 - return -ENOMEM; 371 + if (skb == NULL) { 372 + err = -ENOMEM; 373 + goto out; 374 + } 374 375 375 376 skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE); 376 377 ··· 382 379 383 380 nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_TX); 384 381 385 - return nfc_data_exchange(dev, local->target_idx, skb, 382 + err = nfc_data_exchange(dev, local->target_idx, skb, 386 383 nfc_llcp_recv, local); 384 + out: 385 + nfc_llcp_local_put(local); 386 + return err; 387 387 } 388 388 389 389 int nfc_llcp_send_connect(struct nfc_llcp_sock *sock)
+43 -8
net/nfc/llcp_core.c
··· 17 17 static u8 llcp_magic[3] = {0x46, 0x66, 0x6d}; 18 18 19 19 static LIST_HEAD(llcp_devices); 20 + /* Protects llcp_devices list */ 21 + static DEFINE_SPINLOCK(llcp_devices_lock); 20 22 21 23 static void nfc_llcp_rx_skb(struct nfc_llcp_local *local, struct sk_buff *skb); 22 24 ··· 143 141 write_unlock(&local->raw_sockets.lock); 144 142 } 145 143 146 - struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local) 144 + static struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local) 147 145 { 148 146 kref_get(&local->ref); 149 147 ··· 171 169 172 170 local = container_of(ref, struct nfc_llcp_local, ref); 173 171 174 - list_del(&local->list); 175 172 local_cleanup(local); 176 173 kfree(local); 177 174 } ··· 283 282 struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev) 284 283 { 285 284 struct nfc_llcp_local *local; 285 + struct nfc_llcp_local *res = NULL; 286 286 287 + spin_lock(&llcp_devices_lock); 287 288 list_for_each_entry(local, &llcp_devices, list) 288 - if (local->dev == dev) 289 - return local; 289 + if (local->dev == dev) { 290 + res = nfc_llcp_local_get(local); 291 + break; 292 + } 293 + spin_unlock(&llcp_devices_lock); 290 294 291 - pr_debug("No device found\n"); 295 + return res; 296 + } 297 + 298 + static struct nfc_llcp_local *nfc_llcp_remove_local(struct nfc_dev *dev) 299 + { 300 + struct nfc_llcp_local *local, *tmp; 301 + 302 + spin_lock(&llcp_devices_lock); 303 + list_for_each_entry_safe(local, tmp, &llcp_devices, list) 304 + if (local->dev == dev) { 305 + list_del(&local->list); 306 + spin_unlock(&llcp_devices_lock); 307 + return local; 308 + } 309 + spin_unlock(&llcp_devices_lock); 310 + 311 + pr_warn("Shutting down device not found\n"); 292 312 293 313 return NULL; 294 314 } ··· 630 608 631 609 *general_bytes_len = local->gb_len; 632 610 611 + nfc_llcp_local_put(local); 612 + 633 613 return local->gb; 634 614 } 635 615 636 616 int nfc_llcp_set_remote_gb(struct nfc_dev *dev, const u8 *gb, u8 gb_len) 637 617 { 638 618 struct nfc_llcp_local *local; 619 + int err; 639 620 640 621 if (gb_len < 3 || gb_len > NFC_MAX_GT_LEN) 641 622 return -EINVAL; ··· 655 630 656 631 if (memcmp(local->remote_gb, llcp_magic, 3)) { 657 632 pr_err("MAC does not support LLCP\n"); 658 - return -EINVAL; 633 + err = -EINVAL; 634 + goto out; 659 635 } 660 636 661 - return nfc_llcp_parse_gb_tlv(local, 637 + err = nfc_llcp_parse_gb_tlv(local, 662 638 &local->remote_gb[3], 663 639 local->remote_gb_len - 3); 640 + out: 641 + nfc_llcp_local_put(local); 642 + return err; 664 643 } 665 644 666 645 static u8 nfc_llcp_dsap(const struct sk_buff *pdu) ··· 1546 1517 1547 1518 __nfc_llcp_recv(local, skb); 1548 1519 1520 + nfc_llcp_local_put(local); 1521 + 1549 1522 return 0; 1550 1523 } 1551 1524 ··· 1564 1533 1565 1534 /* Close and purge all existing sockets */ 1566 1535 nfc_llcp_socket_release(local, true, 0); 1536 + 1537 + nfc_llcp_local_put(local); 1567 1538 } 1568 1539 1569 1540 void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx, ··· 1591 1558 mod_timer(&local->link_timer, 1592 1559 jiffies + msecs_to_jiffies(local->remote_lto)); 1593 1560 } 1561 + 1562 + nfc_llcp_local_put(local); 1594 1563 } 1595 1564 1596 1565 int nfc_llcp_register_device(struct nfc_dev *ndev) ··· 1643 1608 1644 1609 void nfc_llcp_unregister_device(struct nfc_dev *dev) 1645 1610 { 1646 - struct nfc_llcp_local *local = nfc_llcp_find_local(dev); 1611 + struct nfc_llcp_local *local = nfc_llcp_remove_local(dev); 1647 1612 1648 1613 if (local == NULL) { 1649 1614 pr_debug("No such device\n");
+10 -8
net/nfc/llcp_sock.c
··· 99 99 } 100 100 101 101 llcp_sock->dev = dev; 102 - llcp_sock->local = nfc_llcp_local_get(local); 102 + llcp_sock->local = local; 103 103 llcp_sock->nfc_protocol = llcp_addr.nfc_protocol; 104 104 llcp_sock->service_name_len = min_t(unsigned int, 105 105 llcp_addr.service_name_len, ··· 186 186 } 187 187 188 188 llcp_sock->dev = dev; 189 - llcp_sock->local = nfc_llcp_local_get(local); 189 + llcp_sock->local = local; 190 190 llcp_sock->nfc_protocol = llcp_addr.nfc_protocol; 191 191 192 192 nfc_llcp_sock_link(&local->raw_sockets, sk); ··· 696 696 if (dev->dep_link_up == false) { 697 697 ret = -ENOLINK; 698 698 device_unlock(&dev->dev); 699 - goto put_dev; 699 + goto sock_llcp_put_local; 700 700 } 701 701 device_unlock(&dev->dev); 702 702 703 703 if (local->rf_mode == NFC_RF_INITIATOR && 704 704 addr->target_idx != local->target_idx) { 705 705 ret = -ENOLINK; 706 - goto put_dev; 706 + goto sock_llcp_put_local; 707 707 } 708 708 709 709 llcp_sock->dev = dev; 710 - llcp_sock->local = nfc_llcp_local_get(local); 710 + llcp_sock->local = local; 711 711 llcp_sock->ssap = nfc_llcp_get_local_ssap(local); 712 712 if (llcp_sock->ssap == LLCP_SAP_MAX) { 713 713 ret = -ENOMEM; 714 - goto sock_llcp_put_local; 714 + goto sock_llcp_nullify; 715 715 } 716 716 717 717 llcp_sock->reserved_ssap = llcp_sock->ssap; ··· 757 757 sock_llcp_release: 758 758 nfc_llcp_put_ssap(local, llcp_sock->ssap); 759 759 760 - sock_llcp_put_local: 761 - nfc_llcp_local_put(llcp_sock->local); 760 + sock_llcp_nullify: 762 761 llcp_sock->local = NULL; 763 762 llcp_sock->dev = NULL; 763 + 764 + sock_llcp_put_local: 765 + nfc_llcp_local_put(local); 764 766 765 767 put_dev: 766 768 nfc_put_device(dev);
+15 -5
net/nfc/netlink.c
··· 1039 1039 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 1040 1040 if (!msg) { 1041 1041 rc = -ENOMEM; 1042 - goto exit; 1042 + goto put_local; 1043 1043 } 1044 1044 1045 1045 rc = nfc_genl_send_params(msg, local, info->snd_portid, info->snd_seq); 1046 + 1047 + put_local: 1048 + nfc_llcp_local_put(local); 1046 1049 1047 1050 exit: 1048 1051 device_unlock(&dev->dev); ··· 1108 1105 if (info->attrs[NFC_ATTR_LLC_PARAM_LTO]) { 1109 1106 if (dev->dep_link_up) { 1110 1107 rc = -EINPROGRESS; 1111 - goto exit; 1108 + goto put_local; 1112 1109 } 1113 1110 1114 1111 local->lto = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_LTO]); ··· 1119 1116 1120 1117 if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX]) 1121 1118 local->miux = cpu_to_be16(miux); 1119 + 1120 + put_local: 1121 + nfc_llcp_local_put(local); 1122 1122 1123 1123 exit: 1124 1124 device_unlock(&dev->dev); ··· 1178 1172 1179 1173 if (rc != 0) { 1180 1174 rc = -EINVAL; 1181 - goto exit; 1175 + goto put_local; 1182 1176 } 1183 1177 1184 1178 if (!sdp_attrs[NFC_SDP_ATTR_URI]) ··· 1197 1191 sdreq = nfc_llcp_build_sdreq_tlv(tid, uri, uri_len); 1198 1192 if (sdreq == NULL) { 1199 1193 rc = -ENOMEM; 1200 - goto exit; 1194 + goto put_local; 1201 1195 } 1202 1196 1203 1197 tlvs_len += sdreq->tlv_len; ··· 1207 1201 1208 1202 if (hlist_empty(&sdreq_list)) { 1209 1203 rc = -EINVAL; 1210 - goto exit; 1204 + goto put_local; 1211 1205 } 1212 1206 1213 1207 rc = nfc_llcp_send_snl_sdreq(local, &sdreq_list, tlvs_len); 1208 + 1209 + put_local: 1210 + nfc_llcp_local_put(local); 1211 + 1214 1212 exit: 1215 1213 device_unlock(&dev->dev); 1216 1214
+1
net/nfc/nfc.h
··· 52 52 u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len); 53 53 int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb); 54 54 struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev); 55 + int nfc_llcp_local_put(struct nfc_llcp_local *local); 55 56 int __init nfc_llcp_init(void); 56 57 void nfc_llcp_exit(void); 57 58 void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp);