Patch "net: nfc: Fix use-after-free caused by nfc_llcp_find_local" has been added to the 5.15-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    net: nfc: Fix use-after-free caused by nfc_llcp_find_local

to the 5.15-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     net-nfc-fix-use-after-free-caused-by-nfc_llcp_find_l.patch
and it can be found in the queue-5.15 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit 5e4ccd95487026c4995cb4849eb7f7af07122550
Author: Lin Ma <linma@xxxxxxxxxx>
Date:   Sun Jun 25 17:10:07 2023 +0800

    net: nfc: Fix use-after-free caused by nfc_llcp_find_local
    
    [ Upstream commit 6709d4b7bc2e079241fdef15d1160581c5261c10 ]
    
    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@xxxxxxxxxx>
    Reviewed-by: Simon Horman <simon.horman@xxxxxxxxxxxx>
    Signed-off-by: David S. Miller <davem@xxxxxxxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/net/nfc/llcp.h b/net/nfc/llcp.h
index d49d4bf2e37c8..a81893bc06ce8 100644
--- a/net/nfc/llcp.h
+++ b/net/nfc/llcp.h
@@ -202,7 +202,6 @@ 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);
-struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local);
 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);
diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c
index bb9f40563ff63..5b8754ae7d3af 100644
--- a/net/nfc/llcp_commands.c
+++ b/net/nfc/llcp_commands.c
@@ -361,6 +361,7 @@ int nfc_llcp_send_symm(struct nfc_dev *dev)
 	struct sk_buff *skb;
 	struct nfc_llcp_local *local;
 	u16 size = 0;
+	int err;
 
 	pr_debug("Sending SYMM\n");
 
@@ -372,8 +373,10 @@ int nfc_llcp_send_symm(struct nfc_dev *dev)
 	size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE;
 
 	skb = alloc_skb(size, GFP_KERNEL);
-	if (skb == NULL)
-		return -ENOMEM;
+	if (skb == NULL) {
+		err = -ENOMEM;
+		goto out;
+	}
 
 	skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE);
 
@@ -383,8 +386,11 @@ int nfc_llcp_send_symm(struct nfc_dev *dev)
 
 	nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_TX);
 
-	return nfc_data_exchange(dev, local->target_idx, skb,
+	err = nfc_data_exchange(dev, local->target_idx, skb,
 				 nfc_llcp_recv, local);
+out:
+	nfc_llcp_local_put(local);
+	return err;
 }
 
 int nfc_llcp_send_connect(struct nfc_llcp_sock *sock)
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index fd43e75abd948..ddfd159f64e13 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -17,6 +17,8 @@
 static u8 llcp_magic[3] = {0x46, 0x66, 0x6d};
 
 static LIST_HEAD(llcp_devices);
+/* Protects llcp_devices list */
+static DEFINE_SPINLOCK(llcp_devices_lock);
 
 static void nfc_llcp_rx_skb(struct nfc_llcp_local *local, struct sk_buff *skb);
 
@@ -143,7 +145,7 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool device,
 	write_unlock(&local->raw_sockets.lock);
 }
 
-struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local)
+static struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local)
 {
 	kref_get(&local->ref);
 
@@ -171,7 +173,6 @@ static void local_release(struct kref *ref)
 
 	local = container_of(ref, struct nfc_llcp_local, ref);
 
-	list_del(&local->list);
 	local_cleanup(local);
 	kfree(local);
 }
@@ -284,12 +285,33 @@ static void nfc_llcp_sdreq_timer(struct timer_list *t)
 struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev)
 {
 	struct nfc_llcp_local *local;
+	struct nfc_llcp_local *res = NULL;
 
+	spin_lock(&llcp_devices_lock);
 	list_for_each_entry(local, &llcp_devices, list)
-		if (local->dev == dev)
+		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);
 			return local;
+		}
+	spin_unlock(&llcp_devices_lock);
 
-	pr_debug("No device found\n");
+	pr_warn("Shutting down device not found\n");
 
 	return NULL;
 }
@@ -610,12 +632,15 @@ u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len)
 
 	*general_bytes_len = local->gb_len;
 
+	nfc_llcp_local_put(local);
+
 	return local->gb;
 }
 
 int nfc_llcp_set_remote_gb(struct nfc_dev *dev, const u8 *gb, u8 gb_len)
 {
 	struct nfc_llcp_local *local;
+	int err;
 
 	if (gb_len < 3 || gb_len > NFC_MAX_GT_LEN)
 		return -EINVAL;
@@ -632,12 +657,16 @@ int nfc_llcp_set_remote_gb(struct nfc_dev *dev, const u8 *gb, u8 gb_len)
 
 	if (memcmp(local->remote_gb, llcp_magic, 3)) {
 		pr_err("MAC does not support LLCP\n");
-		return -EINVAL;
+		err = -EINVAL;
+		goto out;
 	}
 
-	return nfc_llcp_parse_gb_tlv(local,
+	err = nfc_llcp_parse_gb_tlv(local,
 				     &local->remote_gb[3],
 				     local->remote_gb_len - 3);
+out:
+	nfc_llcp_local_put(local);
+	return err;
 }
 
 static u8 nfc_llcp_dsap(const struct sk_buff *pdu)
@@ -1527,6 +1556,8 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
 
 	__nfc_llcp_recv(local, skb);
 
+	nfc_llcp_local_put(local);
+
 	return 0;
 }
 
@@ -1543,6 +1574,8 @@ void nfc_llcp_mac_is_down(struct nfc_dev *dev)
 
 	/* Close and purge all existing sockets */
 	nfc_llcp_socket_release(local, true, 0);
+
+	nfc_llcp_local_put(local);
 }
 
 void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx,
@@ -1568,6 +1601,8 @@ void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx,
 		mod_timer(&local->link_timer,
 			  jiffies + msecs_to_jiffies(local->remote_lto));
 	}
+
+	nfc_llcp_local_put(local);
 }
 
 int nfc_llcp_register_device(struct nfc_dev *ndev)
@@ -1618,7 +1653,7 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
 
 void nfc_llcp_unregister_device(struct nfc_dev *dev)
 {
-	struct nfc_llcp_local *local = nfc_llcp_find_local(dev);
+	struct nfc_llcp_local *local = nfc_llcp_remove_local(dev);
 
 	if (local == NULL) {
 		pr_debug("No such device\n");
diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c
index fdf0856182c65..6e1fba2084930 100644
--- a/net/nfc/llcp_sock.c
+++ b/net/nfc/llcp_sock.c
@@ -99,7 +99,7 @@ static int llcp_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
 	}
 
 	llcp_sock->dev = dev;
-	llcp_sock->local = nfc_llcp_local_get(local);
+	llcp_sock->local = local;
 	llcp_sock->nfc_protocol = llcp_addr.nfc_protocol;
 	llcp_sock->service_name_len = min_t(unsigned int,
 					    llcp_addr.service_name_len,
@@ -181,7 +181,7 @@ static int llcp_raw_sock_bind(struct socket *sock, struct sockaddr *addr,
 	}
 
 	llcp_sock->dev = dev;
-	llcp_sock->local = nfc_llcp_local_get(local);
+	llcp_sock->local = local;
 	llcp_sock->nfc_protocol = llcp_addr.nfc_protocol;
 
 	nfc_llcp_sock_link(&local->raw_sockets, sk);
@@ -698,22 +698,22 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr,
 	if (dev->dep_link_up == false) {
 		ret = -ENOLINK;
 		device_unlock(&dev->dev);
-		goto put_dev;
+		goto sock_llcp_put_local;
 	}
 	device_unlock(&dev->dev);
 
 	if (local->rf_mode == NFC_RF_INITIATOR &&
 	    addr->target_idx != local->target_idx) {
 		ret = -ENOLINK;
-		goto put_dev;
+		goto sock_llcp_put_local;
 	}
 
 	llcp_sock->dev = dev;
-	llcp_sock->local = nfc_llcp_local_get(local);
+	llcp_sock->local = local;
 	llcp_sock->ssap = nfc_llcp_get_local_ssap(local);
 	if (llcp_sock->ssap == LLCP_SAP_MAX) {
 		ret = -ENOMEM;
-		goto sock_llcp_put_local;
+		goto sock_llcp_nullify;
 	}
 
 	llcp_sock->reserved_ssap = llcp_sock->ssap;
@@ -759,11 +759,13 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr,
 sock_llcp_release:
 	nfc_llcp_put_ssap(local, llcp_sock->ssap);
 
-sock_llcp_put_local:
-	nfc_llcp_local_put(llcp_sock->local);
+sock_llcp_nullify:
 	llcp_sock->local = NULL;
 	llcp_sock->dev = NULL;
 
+sock_llcp_put_local:
+	nfc_llcp_local_put(local);
+
 put_dev:
 	nfc_put_device(dev);
 
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index 9bc0ab759ea44..eb4f70a827e4d 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -1039,11 +1039,14 @@ static int nfc_genl_llc_get_params(struct sk_buff *skb, struct genl_info *info)
 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 	if (!msg) {
 		rc = -ENOMEM;
-		goto exit;
+		goto put_local;
 	}
 
 	rc = nfc_genl_send_params(msg, local, info->snd_portid, info->snd_seq);
 
+put_local:
+	nfc_llcp_local_put(local);
+
 exit:
 	device_unlock(&dev->dev);
 
@@ -1105,7 +1108,7 @@ static int nfc_genl_llc_set_params(struct sk_buff *skb, struct genl_info *info)
 	if (info->attrs[NFC_ATTR_LLC_PARAM_LTO]) {
 		if (dev->dep_link_up) {
 			rc = -EINPROGRESS;
-			goto exit;
+			goto put_local;
 		}
 
 		local->lto = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_LTO]);
@@ -1117,6 +1120,9 @@ static int nfc_genl_llc_set_params(struct sk_buff *skb, struct genl_info *info)
 	if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX])
 		local->miux = cpu_to_be16(miux);
 
+put_local:
+	nfc_llcp_local_put(local);
+
 exit:
 	device_unlock(&dev->dev);
 
@@ -1172,7 +1178,7 @@ static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info)
 
 		if (rc != 0) {
 			rc = -EINVAL;
-			goto exit;
+			goto put_local;
 		}
 
 		if (!sdp_attrs[NFC_SDP_ATTR_URI])
@@ -1191,7 +1197,7 @@ static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info)
 		sdreq = nfc_llcp_build_sdreq_tlv(tid, uri, uri_len);
 		if (sdreq == NULL) {
 			rc = -ENOMEM;
-			goto exit;
+			goto put_local;
 		}
 
 		tlvs_len += sdreq->tlv_len;
@@ -1201,10 +1207,14 @@ static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info)
 
 	if (hlist_empty(&sdreq_list)) {
 		rc = -EINVAL;
-		goto exit;
+		goto put_local;
 	}
 
 	rc = nfc_llcp_send_snl_sdreq(local, &sdreq_list, tlvs_len);
+
+put_local:
+	nfc_llcp_local_put(local);
+
 exit:
 	device_unlock(&dev->dev);
 
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h
index de2ec66d7e83a..0b1e6466f4fbf 100644
--- a/net/nfc/nfc.h
+++ b/net/nfc/nfc.h
@@ -52,6 +52,7 @@ int nfc_llcp_set_remote_gb(struct nfc_dev *dev, const u8 *gb, u8 gb_len);
 u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len);
 int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb);
 struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev);
+int nfc_llcp_local_put(struct nfc_llcp_local *local);
 int __init nfc_llcp_init(void);
 void nfc_llcp_exit(void);
 void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp);



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux