mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 14:21:47 +00:00
6709d4b7bc
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:52feb444a9
("NFC: Extend netlink interface for LTO, RW, and MIUX parameters support") Fixes:c7aa12252f
("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>
253 lines
6.7 KiB
C
253 lines
6.7 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/*
|
|
* Copyright (C) 2011 Intel Corporation. All rights reserved.
|
|
*/
|
|
|
|
enum llcp_state {
|
|
LLCP_CONNECTED = 1, /* wait_for_packet() wants that */
|
|
LLCP_CONNECTING,
|
|
LLCP_CLOSED,
|
|
LLCP_BOUND,
|
|
LLCP_LISTEN,
|
|
};
|
|
|
|
#define LLCP_DEFAULT_LTO 100
|
|
#define LLCP_DEFAULT_RW 1
|
|
#define LLCP_DEFAULT_MIU 128
|
|
|
|
#define LLCP_MAX_LTO 0xff
|
|
#define LLCP_MAX_RW 15
|
|
#define LLCP_MAX_MIUX 0x7ff
|
|
#define LLCP_MAX_MIU (LLCP_MAX_MIUX + 128)
|
|
|
|
#define LLCP_WKS_NUM_SAP 16
|
|
#define LLCP_SDP_NUM_SAP 16
|
|
#define LLCP_LOCAL_NUM_SAP 32
|
|
#define LLCP_LOCAL_SAP_OFFSET (LLCP_WKS_NUM_SAP + LLCP_SDP_NUM_SAP)
|
|
#define LLCP_MAX_SAP (LLCP_WKS_NUM_SAP + LLCP_SDP_NUM_SAP + LLCP_LOCAL_NUM_SAP)
|
|
#define LLCP_SDP_UNBOUND (LLCP_MAX_SAP + 1)
|
|
|
|
struct nfc_llcp_sock;
|
|
|
|
struct llcp_sock_list {
|
|
struct hlist_head head;
|
|
rwlock_t lock;
|
|
};
|
|
|
|
struct nfc_llcp_sdp_tlv {
|
|
u8 *tlv;
|
|
u8 tlv_len;
|
|
|
|
char *uri;
|
|
u8 tid;
|
|
u8 sap;
|
|
|
|
unsigned long time;
|
|
|
|
struct hlist_node node;
|
|
};
|
|
|
|
struct nfc_llcp_local {
|
|
struct list_head list;
|
|
struct nfc_dev *dev;
|
|
|
|
struct kref ref;
|
|
|
|
struct mutex sdp_lock;
|
|
|
|
struct timer_list link_timer;
|
|
struct sk_buff_head tx_queue;
|
|
struct work_struct tx_work;
|
|
struct work_struct rx_work;
|
|
struct sk_buff *rx_pending;
|
|
struct work_struct timeout_work;
|
|
|
|
u32 target_idx;
|
|
u8 rf_mode;
|
|
u8 comm_mode;
|
|
u8 lto;
|
|
u8 rw;
|
|
__be16 miux;
|
|
unsigned long local_wks; /* Well known services */
|
|
unsigned long local_sdp; /* Local services */
|
|
unsigned long local_sap; /* Local SAPs, not available for discovery */
|
|
atomic_t local_sdp_cnt[LLCP_SDP_NUM_SAP];
|
|
|
|
/* local */
|
|
u8 gb[NFC_MAX_GT_LEN];
|
|
u8 gb_len;
|
|
|
|
/* remote */
|
|
u8 remote_gb[NFC_MAX_GT_LEN];
|
|
u8 remote_gb_len;
|
|
|
|
u8 remote_version;
|
|
u16 remote_miu;
|
|
u16 remote_lto;
|
|
u8 remote_opt;
|
|
u16 remote_wks;
|
|
|
|
struct mutex sdreq_lock;
|
|
struct hlist_head pending_sdreqs;
|
|
struct timer_list sdreq_timer;
|
|
struct work_struct sdreq_timeout_work;
|
|
u8 sdreq_next_tid;
|
|
|
|
/* sockets array */
|
|
struct llcp_sock_list sockets;
|
|
struct llcp_sock_list connecting_sockets;
|
|
struct llcp_sock_list raw_sockets;
|
|
};
|
|
|
|
struct nfc_llcp_sock {
|
|
struct sock sk;
|
|
struct nfc_dev *dev;
|
|
struct nfc_llcp_local *local;
|
|
u32 target_idx;
|
|
u32 nfc_protocol;
|
|
|
|
/* Link parameters */
|
|
u8 ssap;
|
|
u8 dsap;
|
|
char *service_name;
|
|
size_t service_name_len;
|
|
u8 rw;
|
|
__be16 miux;
|
|
|
|
|
|
/* Remote link parameters */
|
|
u8 remote_rw;
|
|
u16 remote_miu;
|
|
|
|
/* Link variables */
|
|
u8 send_n;
|
|
u8 send_ack_n;
|
|
u8 recv_n;
|
|
u8 recv_ack_n;
|
|
|
|
/* Is the remote peer ready to receive */
|
|
u8 remote_ready;
|
|
|
|
/* Reserved source SAP */
|
|
u8 reserved_ssap;
|
|
|
|
struct sk_buff_head tx_queue;
|
|
struct sk_buff_head tx_pending_queue;
|
|
|
|
struct list_head accept_queue;
|
|
struct sock *parent;
|
|
};
|
|
|
|
struct nfc_llcp_ui_cb {
|
|
__u8 dsap;
|
|
__u8 ssap;
|
|
};
|
|
|
|
#define nfc_llcp_ui_skb_cb(__skb) ((struct nfc_llcp_ui_cb *)&((__skb)->cb[0]))
|
|
|
|
#define nfc_llcp_sock(sk) ((struct nfc_llcp_sock *) (sk))
|
|
#define nfc_llcp_dev(sk) (nfc_llcp_sock((sk))->dev)
|
|
|
|
#define LLCP_HEADER_SIZE 2
|
|
#define LLCP_SEQUENCE_SIZE 1
|
|
#define LLCP_AGF_PDU_HEADER_SIZE 2
|
|
|
|
/* LLCP versions: 1.1 is 1.0 plus SDP */
|
|
#define LLCP_VERSION_10 0x10
|
|
#define LLCP_VERSION_11 0x11
|
|
|
|
/* LLCP PDU types */
|
|
#define LLCP_PDU_SYMM 0x0
|
|
#define LLCP_PDU_PAX 0x1
|
|
#define LLCP_PDU_AGF 0x2
|
|
#define LLCP_PDU_UI 0x3
|
|
#define LLCP_PDU_CONNECT 0x4
|
|
#define LLCP_PDU_DISC 0x5
|
|
#define LLCP_PDU_CC 0x6
|
|
#define LLCP_PDU_DM 0x7
|
|
#define LLCP_PDU_FRMR 0x8
|
|
#define LLCP_PDU_SNL 0x9
|
|
#define LLCP_PDU_I 0xc
|
|
#define LLCP_PDU_RR 0xd
|
|
#define LLCP_PDU_RNR 0xe
|
|
|
|
/* Parameters TLV types */
|
|
#define LLCP_TLV_VERSION 0x1
|
|
#define LLCP_TLV_MIUX 0x2
|
|
#define LLCP_TLV_WKS 0x3
|
|
#define LLCP_TLV_LTO 0x4
|
|
#define LLCP_TLV_RW 0x5
|
|
#define LLCP_TLV_SN 0x6
|
|
#define LLCP_TLV_OPT 0x7
|
|
#define LLCP_TLV_SDREQ 0x8
|
|
#define LLCP_TLV_SDRES 0x9
|
|
#define LLCP_TLV_MAX 0xa
|
|
|
|
/* Well known LLCP SAP */
|
|
#define LLCP_SAP_SDP 0x1
|
|
#define LLCP_SAP_IP 0x2
|
|
#define LLCP_SAP_OBEX 0x3
|
|
#define LLCP_SAP_SNEP 0x4
|
|
#define LLCP_SAP_MAX 0xff
|
|
|
|
/* Disconnection reason code */
|
|
#define LLCP_DM_DISC 0x00
|
|
#define LLCP_DM_NOCONN 0x01
|
|
#define LLCP_DM_NOBOUND 0x02
|
|
#define LLCP_DM_REJ 0x03
|
|
|
|
|
|
void nfc_llcp_sock_link(struct llcp_sock_list *l, struct sock *s);
|
|
void nfc_llcp_sock_unlink(struct llcp_sock_list *l, struct sock *s);
|
|
void nfc_llcp_socket_remote_param_init(struct nfc_llcp_sock *sock);
|
|
struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev);
|
|
int nfc_llcp_local_put(struct nfc_llcp_local *local);
|
|
u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local,
|
|
struct nfc_llcp_sock *sock);
|
|
u8 nfc_llcp_get_local_ssap(struct nfc_llcp_local *local);
|
|
void nfc_llcp_put_ssap(struct nfc_llcp_local *local, u8 ssap);
|
|
int nfc_llcp_queue_i_frames(struct nfc_llcp_sock *sock);
|
|
void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local,
|
|
struct sk_buff *skb, u8 direction);
|
|
|
|
/* Sock API */
|
|
struct sock *nfc_llcp_sock_alloc(struct socket *sock, int type, gfp_t gfp, int kern);
|
|
void nfc_llcp_sock_free(struct nfc_llcp_sock *sock);
|
|
void nfc_llcp_accept_unlink(struct sock *sk);
|
|
void nfc_llcp_accept_enqueue(struct sock *parent, struct sock *sk);
|
|
struct sock *nfc_llcp_accept_dequeue(struct sock *sk, struct socket *newsock);
|
|
|
|
/* TLV API */
|
|
int nfc_llcp_parse_gb_tlv(struct nfc_llcp_local *local,
|
|
const u8 *tlv_array, u16 tlv_array_len);
|
|
int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock,
|
|
const u8 *tlv_array, u16 tlv_array_len);
|
|
|
|
/* Commands API */
|
|
void nfc_llcp_recv(void *data, struct sk_buff *skb, int err);
|
|
u8 *nfc_llcp_build_tlv(u8 type, const u8 *value, u8 value_length, u8 *tlv_length);
|
|
struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdres_tlv(u8 tid, u8 sap);
|
|
struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, const char *uri,
|
|
size_t uri_len);
|
|
void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp);
|
|
void nfc_llcp_free_sdp_tlv_list(struct hlist_head *sdp_head);
|
|
void nfc_llcp_recv(void *data, struct sk_buff *skb, int err);
|
|
int nfc_llcp_send_symm(struct nfc_dev *dev);
|
|
int nfc_llcp_send_connect(struct nfc_llcp_sock *sock);
|
|
int nfc_llcp_send_cc(struct nfc_llcp_sock *sock);
|
|
int nfc_llcp_send_snl_sdres(struct nfc_llcp_local *local,
|
|
struct hlist_head *tlv_list, size_t tlvs_len);
|
|
int nfc_llcp_send_snl_sdreq(struct nfc_llcp_local *local,
|
|
struct hlist_head *tlv_list, size_t tlvs_len);
|
|
int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason);
|
|
int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock);
|
|
int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
|
|
struct msghdr *msg, size_t len);
|
|
int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap,
|
|
struct msghdr *msg, size_t len);
|
|
int nfc_llcp_send_rr(struct nfc_llcp_sock *sock);
|
|
|
|
/* Socket API */
|
|
int __init nfc_llcp_sock_init(void);
|
|
void nfc_llcp_sock_exit(void);
|