mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net v2] nfc: llcp: prevent resource leak on repeated connect after DM
@ 2026-09-25 18:48 Aldo Ariel Panzardo
  2026-09-27 22:32 ` [syzbot ci] " syzbot ci
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Aldo Ariel Panzardo @ 2026-09-25 18:48 UTC (permalink / raw)
  To: david, davem, edumazet, kuba, pabeni
  Cc: horms, oe-linux-nfc, netdev, linux-kernel, stable,
	Aldo Ariel Panzardo, Sashiko

A nonblocking connect can return while the socket is still connecting.
If the peer rejects the connection with a DM PDU, nfc_llcp_recv_dm()
changes the socket state to LLCP_CLOSED.  A subsequent connect() on that
socket currently overwrites the previous local, SAP and service name
without releasing them.  Repeated retries therefore leak local references
and SAP allocations until no SAPs remain.

Release any resources left on the socket before obtaining resources for a
new connection.  A closed socket can also retain the device reference held
by an asynchronous connect, so drop that reference as well.  Bound sockets
do not hold the device reference, so release it only when reconnecting from
LLCP_CLOSED.

Fixes: d646960f7986 ("NFC: Initial LLCP support")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Link: https://lore.kernel.org/all/20260923133339.2518641-1-qwe.aldo@gmail.com/
Cc: stable@vger.kernel.org
Signed-off-by: Aldo Ariel Panzardo <qwe.aldo@gmail.com>
---
v2: fix author name (v1 was sent with an incorrect From: field)

  net/nfc/llcp_sock.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c
index 5558d8a..e76361e 100644
--- a/net/nfc/llcp_sock.c
+++ b/net/nfc/llcp_sock.c
@@ -690,6 +690,23 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr_unsized *_addr
 		goto error;
 	}
 
+	if (sk->sk_state == LLCP_CLOSED) {
+		/* Release resources retained by a previous failed connection. */
+		if (llcp_sock->local) {
+			if (llcp_sock->reserved_ssap < LLCP_SAP_MAX)
+				nfc_llcp_put_ssap(llcp_sock->local, llcp_sock->ssap);
+			nfc_llcp_local_put(llcp_sock->local);
+		}
+		if (llcp_sock->dev)
+			nfc_put_device(llcp_sock->dev);
+		kfree(llcp_sock->service_name);
+		llcp_sock->local = NULL;
+		llcp_sock->dev = NULL;
+		llcp_sock->service_name = NULL;
+		llcp_sock->service_name_len = 0;
+		llcp_sock->reserved_ssap = LLCP_SAP_MAX;
+	}
+
 	dev = nfc_get_device(addr->dev_idx);
 	if (dev == NULL) {
 		ret = -ENODEV;
-- 
2.43.0

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [syzbot ci] Re: nfc: llcp: prevent resource leak on repeated connect after DM
  2026-09-25 18:48 [PATCH net v2] nfc: llcp: prevent resource leak on repeated connect after DM Aldo Ariel Panzardo
@ 2026-09-27 22:32 ` syzbot ci
  2026-09-28  3:57 ` [PATCH net v3] " Aldo Ariel Panzardo
  2026-09-28  9:53 ` [PATCH net v2] " netdev-bot+sashiko
  2 siblings, 0 replies; 4+ messages in thread
From: syzbot ci @ 2026-09-27 22:32 UTC (permalink / raw)
  To: davem, david, edumazet, horms, kuba, linux-kernel, netdev,
	oe-linux-nfc, pabeni, qwe.aldo, sashiko-bot, stable
  Cc: syzbot, syzkaller-bugs

syzbot ci has tested the following series

[v2] nfc: llcp: prevent resource leak on repeated connect after DM
https://lore.kernel.org/all/20260925184857.357926-1-qwe.aldo@gmail.com
* [PATCH net v2] nfc: llcp: prevent resource leak on repeated connect after DM

and found the following issues:
* KASAN: slab-use-after-free Read in llcp_sock_connect
* KASAN: slab-use-after-free Read in nfc_remove_device

Full report is available here:
https://ci.syzbot.org/series/5e0cb36e-53b9-46fb-95cf-63d7aa556657

***

KASAN: slab-use-after-free Read in llcp_sock_connect

tree:      net
URL:       https://kernel.googlesource.com/pub/scm/linux/kernel/git/netdev/net.git
base:      415f2044228cc8b948dc04e079aaa86a4d1aa1d3
arch:      amd64
compiler:  Debian clang version 22.1.8 (++20260613092233+e80beda6e255-1~exp1~20260613092250.77), Debian LLD 22.1.8
config:    https://ci.syzbot.org/builds/b9c88e36-7160-4be9-b8c4-0246fd730eef/config
syz repro: https://ci.syzbot.org/findings/64002827-5231-4183-bfca-0d91bded8543/syz_repro

==================================================================
BUG: KASAN: slab-use-after-free in kobject_put+0x2a2/0x550 lib/kobject.c:733
Read of size 1 at addr ffff8881ae430054 by task syz.2.20/5885

CPU: 0 UID: 0 PID: 5885 Comm: syz.2.20 Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
Call Trace:
 <TASK>
 dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
 print_address_description+0x55/0x1e0 mm/kasan/report.c:378
 print_report+0x58/0x70 mm/kasan/report.c:482
 kasan_report+0x117/0x150 mm/kasan/report.c:595
 kobject_put+0x2a2/0x550 lib/kobject.c:733
 nfc_put_device net/nfc/nfc.h:103 [inline]
 llcp_sock_connect+0x2f5/0xc50 net/nfc/llcp_sock.c:701
 connect_socket net/socket.c:2141 [inline]
 __sys_connect_file net/socket.c:2166 [inline]
 __sys_connect+0x316/0x450 net/socket.c:2183
 __do_sys_connect net/socket.c:2189 [inline]
 __se_sys_connect net/socket.c:2186 [inline]
 __x64_sys_connect+0x7a/0x90 net/socket.c:2186
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f012ed9e159
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f012fb8e028 EFLAGS: 00000246 ORIG_RAX: 000000000000002a
RAX: ffffffffffffffda RBX: 00007f012f025fa0 RCX: 00007f012ed9e159
RDX: 0000000000000060 RSI: 0000200000000380 RDI: 0000000000000004
RBP: 00007f012ee3506b R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f012f026038 R14: 00007f012f025fa0 R15: 00007fff3cb60738
 </TASK>

Allocated by task 5885:
 kasan_save_stack mm/kasan/common.c:57 [inline]
 kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
 poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
 __kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
 kasan_kmalloc include/linux/kasan.h:263 [inline]
 __kmalloc_cache_noprof+0x321/0x600 mm/slub.c:5563
 _kmalloc_noprof include/linux/slab.h:991 [inline]
 _kzalloc_noprof include/linux/slab.h:1312 [inline]
 nfc_allocate_device+0x129/0x560 net/nfc/core.c:1065
 nci_allocate_device+0x1da/0x380 net/nfc/nci/core.c:1206
 virtual_ncidev_open+0x75/0x1a0 drivers/nfc/virtual_ncidev.c:141
 misc_open+0x2d5/0x350 drivers/char/misc.c:163
 chrdev_open+0x4d9/0x600 fs/char_dev.c:411
 do_dentry_open+0x816/0x1380 fs/open.c:996
 vfs_open+0x3b/0x340 fs/open.c:1101
 do_open fs/namei.c:4837 [inline]
 path_openat+0x1443/0x1d60 fs/namei.c:5000
 do_file_open+0x23e/0x4a0 fs/namei.c:5029
 do_sys_openat2+0x115/0x200 fs/open.c:1417
 do_sys_open fs/open.c:1423 [inline]
 __do_sys_openat fs/open.c:1439 [inline]
 __se_sys_openat fs/open.c:1434 [inline]
 __x64_sys_openat+0x138/0x170 fs/open.c:1434
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Freed by task 5885:
 kasan_save_stack mm/kasan/common.c:57 [inline]
 kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
 kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584
 poison_slab_object mm/kasan/common.c:253 [inline]
 __kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285
 kasan_slab_free include/linux/kasan.h:235 [inline]
 slab_free_hook mm/slub.c:2748 [inline]
 slab_free mm/slub.c:6508 [inline]
 kfree+0x1c5/0x650 mm/slub.c:6801
 device_release+0xc4/0x1f0 drivers/base/core.c:-1
 kobject_cleanup lib/kobject.c:689 [inline]
 kobject_release lib/kobject.c:720 [inline]
 kref_put include/linux/kref.h:65 [inline]
 kobject_put+0x222/0x550 lib/kobject.c:737
 nfc_put_device net/nfc/nfc.h:103 [inline]
 nfc_llcp_local_put+0xb7/0xe0 net/nfc/llcp_core.c:196
 llcp_sock_connect+0x2bb/0xc50 net/nfc/llcp_sock.c:698
 connect_socket net/socket.c:2141 [inline]
 __sys_connect_file net/socket.c:2166 [inline]
 __sys_connect+0x316/0x450 net/socket.c:2183
 __do_sys_connect net/socket.c:2189 [inline]
 __se_sys_connect net/socket.c:2186 [inline]
 __x64_sys_connect+0x7a/0x90 net/socket.c:2186
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

The buggy address belongs to the object at ffff8881ae430000
 which belongs to the cache kmalloc-2k of size 2048
The buggy address is located 84 bytes inside of
 freed 2048-byte region [ffff8881ae430000, ffff8881ae430800)

The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x1ae430
head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x57ff00000000040(head|node=1|zone=2|lastcpupid=0x7ff)
page_type: f5(slab)
raw: 057ff00000000040 ffff888100042000 dead000000000100 dead000000000122
raw: 0000000000000000 0000000000080008 00000000f5000000 0000000000000000
head: 057ff00000000040 ffff888100042000 dead000000000100 dead000000000122
head: 0000000000000000 0000000000080008 00000000f5000000 0000000000000000
head: 057ff00000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000008
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd20c0(__GFP_IO|__GFP_FS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5818, tgid 5818 (kworker/1:6), ts 69407176292
 set_page_owner include/linux/page_owner.h:33 [inline]
 post_alloc_hook+0x1f9/0x250 mm/page_alloc.c:1871
 prep_new_page mm/page_alloc.c:1879 [inline]
 get_page_from_freelist+0x2209/0x2280 mm/page_alloc.c:3943
 __alloc_frozen_pages_noprof+0x217/0x5a0 mm/page_alloc.c:5436
 alloc_slab_page mm/slub.c:3347 [inline]
 allocate_slab+0x7d/0x620 mm/slub.c:3462
 new_slab mm/slub.c:3513 [inline]
 refill_objects+0x2d5/0x350 mm/slub.c:7417
 refill_sheaf mm/slub.c:2885 [inline]
 __pcs_replace_empty_main+0x2c8/0x6c0 mm/slub.c:4774
 alloc_from_pcs mm/slub.c:4850 [inline]
 slab_alloc_node mm/slub.c:4984 [inline]
 __do_kmalloc_node mm/slub.c:5413 [inline]
 __kmalloc_node_track_caller_noprof+0x548/0x720 mm/slub.c:5545
 kmalloc_reserve net/core/skbuff.c:637 [inline]
 __alloc_skb+0x2c1/0x7a0 net/core/skbuff.c:715
 alloc_skb include/linux/skbuff.h:1384 [inline]
 mld_newpack+0x165/0xcb0 net/ipv6/mcast.c:1810
 add_grhead net/ipv6/mcast.c:1921 [inline]
 add_grec+0x119f/0x19d0 net/ipv6/mcast.c:2060
 mld_send_cr net/ipv6/mcast.c:2185 [inline]
 mld_ifc_work+0x6f1/0xd60 net/ipv6/mcast.c:2739
 process_one_work kernel/workqueue.c:3396 [inline]
 process_scheduled_works+0xc3d/0x1630 kernel/workqueue.c:3479
 worker_thread+0xa47/0xfb0 kernel/workqueue.c:3560
 kthread+0x38b/0x480 kernel/kthread.c:436
 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
page last free pid 5677 tgid 5677 ts 63170557312 stack trace:
 reset_page_owner include/linux/page_owner.h:26 [inline]
 __free_pages_prepare mm/page_alloc.c:1418 [inline]
 free_pages_prepare+0x9eb/0xaf0 mm/page_alloc.c:1463
 __free_contig_range_common+0x174/0x340 mm/page_alloc.c:7072
 __free_contig_range mm/page_alloc.c:7117 [inline]
 free_pages_bulk+0x48/0x120 mm/page_alloc.c:5322
 vm_area_free_pages mm/vmalloc.c:3461 [inline]
 vfree+0x231/0x490 mm/vmalloc.c:3510
 kcov_put kernel/kcov.c:451 [inline]
 kcov_close+0x28/0x50 kernel/kcov.c:556
 __fput+0x418/0xa50 fs/file_table.c:512
 task_work_run+0x1d9/0x270 kernel/task_work.c:233
 exit_task_work include/linux/task_work.h:40 [inline]
 do_exit+0x73a/0x2360 kernel/exit.c:1012
 do_group_exit+0x22d/0x2f0 kernel/exit.c:1155
 get_signal+0x121b/0x12c0 kernel/signal.c:3112
 arch_do_signal_or_restart+0xbb/0x860 arch/x86/kernel/signal.c:337
 __exit_to_user_mode_loop kernel/entry/common.c:66 [inline]
 exit_to_user_mode_loop+0x10e/0x770 kernel/entry/common.c:101
 __exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline]
 syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]
 syscall_exit_to_user_mode include/linux/entry-common.h:343 [inline]
 do_syscall_64+0x328/0x520 arch/x86/entry/syscall_64.c:89
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Memory state around the buggy address:
 ffff8881ae42ff00: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
 ffff8881ae42ff80: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
>ffff8881ae430000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
                                                 ^
 ffff8881ae430080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
 ffff8881ae430100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================


***

KASAN: slab-use-after-free Read in nfc_remove_device

tree:      net
URL:       https://kernel.googlesource.com/pub/scm/linux/kernel/git/netdev/net.git
base:      415f2044228cc8b948dc04e079aaa86a4d1aa1d3
arch:      amd64
compiler:  Debian clang version 22.1.8 (++20260613092233+e80beda6e255-1~exp1~20260613092250.77), Debian LLD 22.1.8
config:    https://ci.syzbot.org/builds/b9c88e36-7160-4be9-b8c4-0246fd730eef/config

==================================================================
BUG: KASAN: slab-use-after-free in sysfs_remove_file_ns+0x3d/0x70
Read of size 8 at addr ffff888177b8a048 by task syz.1.3888/17756

CPU: 0 UID: 0 PID: 17756 Comm: syz.1.3888 Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
Call Trace:
 <TASK>
 dump_stack_lvl+0xe8/0x150
 print_address_description+0x55/0x1e0
 print_report+0x58/0x70
 kasan_report+0x117/0x150
 sysfs_remove_file_ns+0x3d/0x70
 device_del+0x503/0x8f0
 nfc_remove_device+0xa7/0xc0
 virtual_ncidev_close+0x56/0x90
 __fput+0x418/0xa50
 fput_close_sync+0x11f/0x240
 __x64_sys_close+0x7e/0x110
 do_syscall_64+0x166/0x520
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fb42af9e159
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fb42bdab028 EFLAGS: 00000246 ORIG_RAX: 0000000000000003
RAX: ffffffffffffffda RBX: 00007fb42b226090 RCX: 00007fb42af9e159
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000003
RBP: 00007fb42b03506b R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007fb42b226128 R14: 00007fb42b226090 R15: 00007fff6f3d29a8
 </TASK>

Allocated by task 17749:
 kasan_save_track+0x3e/0x80
 __kasan_kmalloc+0x93/0xb0
 __kmalloc_cache_noprof+0x321/0x600
 nfc_allocate_device+0x129/0x560
 nci_allocate_device+0x1da/0x380
 virtual_ncidev_open+0x75/0x1a0
 misc_open+0x2d5/0x350
 chrdev_open+0x4d9/0x600
 do_dentry_open+0x816/0x1380
 vfs_open+0x3b/0x340
 path_openat+0x1443/0x1d60
 do_file_open+0x23e/0x4a0
 do_sys_openat2+0x115/0x200
 __x64_sys_openat+0x138/0x170
 do_syscall_64+0x166/0x520
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Freed by task 17756:
 kasan_save_track+0x3e/0x80
 kasan_save_free_info+0x40/0x50
 __kasan_slab_free+0x5c/0x80
 kfree+0x1c5/0x650
 device_release+0xc4/0x1f0
 kobject_put+0x222/0x550
 device_del+0x4cb/0x8f0
 nfc_remove_device+0xa7/0xc0
 virtual_ncidev_close+0x56/0x90
 __fput+0x418/0xa50
 fput_close_sync+0x11f/0x240
 __x64_sys_close+0x7e/0x110
 do_syscall_64+0x166/0x520
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

The buggy address belongs to the object at ffff888177b8a000
 which belongs to the cache kmalloc-2k of size 2048
The buggy address is located 72 bytes inside of
 freed 2048-byte region [ffff888177b8a000, ffff888177b8a800)

The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x177b88
head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x57ff00000000040(head|node=1|zone=2|lastcpupid=0x7ff)
page_type: f5(slab)
raw: 057ff00000000040 ffff888100042000 dead000000000100 dead000000000122
raw: 0000000000000000 0000000000080008 00000000f5000000 0000000000000000
head: 057ff00000000040 ffff888100042000 dead000000000100 dead000000000122
head: 0000000000000000 0000000000080008 00000000f5000000 0000000000000000
head: 057ff00000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000008
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd28c0(GFP_NOWAIT|__GFP_IO|__GFP_FS|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5596, tgid 5596 (syz-executor), ts 59551528746
 post_alloc_hook+0x1f9/0x250
 get_page_from_freelist+0x2209/0x2280
 __alloc_frozen_pages_noprof+0x217/0x5a0
 allocate_slab+0x7d/0x620
 refill_objects+0x2d5/0x350
 __pcs_replace_empty_main+0x2c8/0x6c0
 __kmalloc_node_track_caller_noprof+0x548/0x720
 pskb_expand_head+0x22c/0x13a0
 netlink_trim+0x1b3/0x2c0
 netlink_broadcast_filtered+0x6d/0xea0
 nlmsg_notify+0xe3/0x1a0
 rtnetlink_event+0x224/0x270
 notifier_call_chain+0x1a5/0x3d0
 netif_set_mac_address+0x393/0x4d0
 do_setlink+0x9ad/0x4670
 rtnl_newlink+0x15a3/0x1c30
page last free pid 15 tgid 15 ts 54085035095 stack trace:
 __free_frozen_pages+0xc93/0xd90
 __folio_put+0x4b3/0x590
 skb_release_data+0x573/0xab0
 __kfree_skb+0x5d/0x210
 e1000_clean+0x4ae/0x2bf0
 __napi_poll+0xaa/0x330
 net_rx_action+0x61d/0xf50
 handle_softirqs+0x226/0x860
 run_ksoftirqd+0x36/0x60
 smpboot_thread_fn+0x565/0xa70
 kthread+0x38b/0x480
 ret_from_fork+0x514/0xb70
 ret_from_fork_asm+0x1a/0x30

Memory state around the buggy address:
 ffff888177b89f00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
 ffff888177b89f80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888177b8a000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
                                              ^
 ffff888177b8a080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
 ffff888177b8a100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================


***

If these findings have caused you to resend the series or submit a
separate fix, please add the following tag to your commit message:
  Tested-by: syzbot@syzkaller.appspotmail.com

---
This report is generated by a bot. It may contain errors.
syzbot ci engineers can be reached at syzkaller@googlegroups.com.

To test a fix for this bug, please reply with `#syz test`
(on a separate line) and attach the patch to the email.

Notes:
- The patch will be applied on top of the tested series (as an
  incremental fix).
- To test a new version of the whole series, please send it directly
  to syzbot@lists.linux.dev.
- Arguments like custom git repos and branches are not supported.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH net v3] nfc: llcp: prevent resource leak on repeated connect after DM
  2026-09-25 18:48 [PATCH net v2] nfc: llcp: prevent resource leak on repeated connect after DM Aldo Ariel Panzardo
  2026-09-27 22:32 ` [syzbot ci] " syzbot ci
@ 2026-09-28  3:57 ` Aldo Ariel Panzardo
  2026-09-28  9:53 ` [PATCH net v2] " netdev-bot+sashiko
  2 siblings, 0 replies; 4+ messages in thread
From: Aldo Ariel Panzardo @ 2026-09-28  3:57 UTC (permalink / raw)
  To: david, davem, edumazet, kuba, pabeni
  Cc: horms, oe-linux-nfc, netdev, linux-kernel, stable,
	syzbot+ci3c472c63e196fe9e, Aldo Ariel Panzardo, Sashiko

A rejected asynchronous connect leaves a local reference, SAP allocation,
service name and device reference on a closed socket. Release them before
retrying connect.

The device pointer alone does not prove ownership: bind() puts its
temporary device reference while retaining the pointer. When device
teardown closes a bound socket, the retry cleanup would put that device
again. Clear the pointer when a bound or listening socket becomes closed,
and after a connected socket's device reference is put. Do the same when
DM closes a bound or listening socket. A DM rejected connect keeps its
device reference until the retry cleanup, so retain that put there.

Fixes: d646960f7986 ("NFC: Initial LLCP support")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Link: https://ci.syzbot.org/findings/64002827-5231-4183-bfca-0d91bded8543/syz_repro
Cc: stable@vger.kernel.org
Signed-off-by: Aldo Ariel Panzardo <qwe.aldo@gmail.com>
---
v3: Clear non-owning or already released device pointers on close, while
    preserving the device put for rejected asynchronous connects.

 net/nfc/llcp_core.c | 12 +++++++++++-
 net/nfc/llcp_sock.c | 17 +++++++++++++++++
 2 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index 199543c47eef..6382aacca69a 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -95,8 +95,14 @@
 
 		nfc_llcp_socket_purge(llcp_sock);
 
-		if (sk->sk_state == LLCP_CONNECTED)
+		if (sk->sk_state == LLCP_CONNECTED) {
 			nfc_put_device(llcp_sock->dev);
+			llcp_sock->dev = NULL;
+		} else if (sk->sk_state == LLCP_BOUND ||
+			   sk->sk_state == LLCP_LISTEN) {
+			/* bind() already dropped its temporary device reference. */
+			llcp_sock->dev = NULL;
+		}
 
 		if (sk->sk_state == LLCP_LISTEN) {
 			struct nfc_llcp_sock *lsk, *n;
@@ -1330,6 +1336,10 @@
 	if (connecting)
 		nfc_llcp_sock_unlink(&local->connecting_sockets, sk);
 
+	/* Bound and listening sockets do not own a device reference. */
+	if (sk->sk_state == LLCP_BOUND || sk->sk_state == LLCP_LISTEN)
+		llcp_sock->dev = NULL;
+
 	sk->sk_err = ENXIO;
 	sk->sk_state = LLCP_CLOSED;
 	sk->sk_state_change(sk);
diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c
index 1e5ee4bcde68..9d76c12aee01 100644
--- a/net/nfc/llcp_sock.c
+++ b/net/nfc/llcp_sock.c
@@ -713,6 +713,23 @@
 	if (sk->sk_state == LLCP_CONNECTING) {
 		ret = -EINPROGRESS;
 		goto error;
+	}
+
+	if (sk->sk_state == LLCP_CLOSED) {
+		/* Release resources retained by a previous failed connection. */
+		if (llcp_sock->local) {
+			if (llcp_sock->reserved_ssap < LLCP_SAP_MAX)
+				nfc_llcp_put_ssap(llcp_sock->local, llcp_sock->ssap);
+			nfc_llcp_local_put(llcp_sock->local);
+		}
+		if (llcp_sock->dev)
+			nfc_put_device(llcp_sock->dev);
+		kfree(llcp_sock->service_name);
+		llcp_sock->local = NULL;
+		llcp_sock->dev = NULL;
+		llcp_sock->service_name = NULL;
+		llcp_sock->service_name_len = 0;
+		llcp_sock->reserved_ssap = LLCP_SAP_MAX;
 	}
 
 	dev = nfc_get_device(addr->dev_idx);
-- 
2.43.0

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH net v2] nfc: llcp: prevent resource leak on repeated connect after DM
  2026-09-25 18:48 [PATCH net v2] nfc: llcp: prevent resource leak on repeated connect after DM Aldo Ariel Panzardo
  2026-09-27 22:32 ` [syzbot ci] " syzbot ci
  2026-09-28  3:57 ` [PATCH net v3] " Aldo Ariel Panzardo
@ 2026-09-28  9:53 ` netdev-bot+sashiko
  2 siblings, 0 replies; 4+ messages in thread
From: netdev-bot+sashiko @ 2026-09-28  9:53 UTC (permalink / raw)
  To: qwe.aldo
  Cc: david, davem, edumazet, kuba, pabeni, horms, oe-linux-nfc,
	netdev, linux-kernel, stable, sashiko-bot

Thank you for your contribution! Sashiko AI review found 4 potential
issue(s) to consider.

Critical: 2 · High: 2 (1) · Medium: 0 · Low: 0

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Critical] The new LLCP_CLOSED cleanup in llcp_sock_connect()
  (net/nfc/llcp_sock.c:725-726, `if (llcp_sock->dev)…
- [Critical] The new cleanup can release resources that a sleeping
  blocking connect() still owns.
- [High] Two paths close a socket but leave it hashed on local->sockets:
  nfc_llcp_recv_disc() (llcp_core.c:1221-1224), and the default branch…
- [High] The leak this patch targets is still reachable through bind().

Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but the retry
  scenario the patch targets can crash the kernel.

--

nfc: llcp: prevent resource leak on repeated connect after DM

Before llcp_sock_connect() takes new resources, this patch releases the
local reference, reserved SAP, service name and device reference still
held by an LLCP socket in LLCP_CLOSED. It targets a nonblocking connect
that a DM PDU rejects and that is then retried.

Link: https://lore.kernel.org/all/20260923133339.2518641-1-qwe.aldo@gmail.com/

> diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c
> index 1e5ee4bcde684..9d76c12aee011 100644
> --- a/net/nfc/llcp_sock.c
> +++ b/net/nfc/llcp_sock.c
> @@ -715,6 +715,23 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr_unsized *_addr
>  		goto error;
>  	}
>  
> +	if (sk->sk_state == LLCP_CLOSED) {
> +		/* Release resources retained by a previous failed connection. */
> +		if (llcp_sock->local) {
> +			if (llcp_sock->reserved_ssap < LLCP_SAP_MAX)
> +				nfc_llcp_put_ssap(llcp_sock->local, llcp_sock->ssap);
> +			nfc_llcp_local_put(llcp_sock->local);
> +		}

[Severity: Critical]
Can this release resources that a blocking connect() on the same socket
still owns?

While sock_wait_state() sleeps it drops the socket lock. The first caller
keeps local and dev in its own stack frame:

Thread A (blocking connect)
llcp_sock_connect()
  sock_wait_state()
    release_sock(sk);
    timeo = schedule_timeout(timeo);

rx worker
nfc_llcp_recv_dm()
  sk->sk_err = ENXIO;
  sk->sk_state = LLCP_CLOSED;
  sk->sk_state_change(sk);

Thread B (same fd, wins lock_sock first)
llcp_sock_connect()
  sk_state == LLCP_CLOSED, so:
    nfc_llcp_put_ssap(llcp_sock->local, llcp_sock->ssap);
    nfc_llcp_local_put(llcp_sock->local);
    nfc_put_device(llcp_sock->dev);
  nfc_get_device(addr->dev_idx) fails with -ENODEV

Thread A
  lock_sock(sk);
  err = sock_error(sk);   /* still -ENXIO */
  goto sock_unlink
    nfc_llcp_put_ssap(local, llcp_sock->ssap);
    nfc_llcp_local_put(local);
    nfc_put_device(dev);

Thread B clears ->local, ->dev and ->reserved_ssap but not ->ssap. Thread
A then frees the same SAP again, which may already belong to another
socket. It also drops the local and dev references a second time.

Could the nfc_llcp_local and nfc_dev refcounts underflow here? That would
free local while it is still on llcp_devices, and free the nfc_dev while
it is still registered.

Without this patch, thread B's failed connect() left the socket unchanged,
so each resource was released only once.

> +		if (llcp_sock->dev)
> +			nfc_put_device(llcp_sock->dev);

[Severity: Critical]
Does a CLOSED socket with a non-NULL llcp_sock->dev always own an nfc_dev
reference? That seems true only after a nonblocking connect that a
DM(REJ/NOBOUND) rejected.

Several existing paths leave the socket in LLCP_CLOSED with llcp_sock->dev
still set, but with the reference already dropped or never taken.

nfc_llcp_recv_disc() already drops the reference for a connected socket:

	if (sk->sk_state == LLCP_CONNECTED) {
		nfc_put_device(local->dev);
		sk->sk_state = LLCP_CLOSED;

nfc_llcp_socket_release() runs from
nfc_dep_link_down()->nfc_llcp_mac_is_down(), and from local_cleanup() on
unregister. It drops dev for CONNECTED sockets. It then moves every
socket, BOUND and LISTEN ones included, to LLCP_CLOSED without clearing
->dev.

llcp_sock_bind() keeps ->dev as a pointer without holding a reference:

	sk->sk_state = LLCP_BOUND;
	nfc_put_device(dev);

Child sockets accepted in nfc_llcp_recv_connect() also take a reference.
DISC or link down later drops it.

connect() only rejects LLCP_CONNECTED and LLCP_CONNECTING, and this
cleanup runs before the dev_idx lookup. So bind(), then a dep link down,
then connect() (even with a bogus dev_idx) would call nfc_put_device() on
a reference the socket does not own.

Doing this on several sockets looks like it can underflow the nfc_dev
refcount and free the device while it is still registered.

The commit message says:

  "Bound sockets do not hold the device reference, so release it only
  when reconnecting from LLCP_CLOSED."

Bound sockets do reach LLCP_CLOSED on link down, though.
llcp_sock_destruct() also assumes the reference is owned only in
LLCP_CONNECTED:

	if (sk->sk_state == LLCP_CONNECTED)
		nfc_put_device(llcp_sock->dev);

Would it be safer to drop the reference and clear llcp_sock->dev in
nfc_llcp_recv_dm(), at the CONNECTING to CLOSED transition, instead of
guessing ownership from the state here?

> +		kfree(llcp_sock->service_name);
> +		llcp_sock->local = NULL;

[Severity: High]
Is the socket always off local->sockets at this point?

nfc_llcp_recv_disc() moves a connected socket to LLCP_CLOSED but leaves it
on local->sockets. The default branch of nfc_llcp_recv_dm() (any reason
other than NOBOUND/REJ) also closes the socket without unlinking it.

For such a socket, this block frees the SAP, drops the local reference and
sets llcp_sock->local = NULL. The rx worker can still find the socket
through nfc_llcp_sock_get(), and the SAP can be given to another socket.

If connect() then fails early (bad dev_idx, nfc_llcp_find_local()
failure, dep link down, or target_idx mismatch), llcp_sock_release()
returns before it unlinks anything:

	local = llcp_sock->local;
	if (local == NULL) {
		err = -ENODEV;
		goto out;
	}

Wouldn't the orphaned socket stay on local->sockets until a link down or
unregister? Before this patch, ->local was still set on those early error
paths, so close() unlinked the socket.

If connect() goes ahead instead, it reaches:

	nfc_llcp_sock_link(&local->connecting_sockets, sk);

At that point sk_node is still linked in local->sockets. Two different
rwlocks protect the two lists, and both hlists look like they would be
corrupted.

That double link existed before this patch. Since this change prepares
CLOSED sockets for reuse, should it also unlink the socket here?

> +		llcp_sock->dev = NULL;
> +		llcp_sock->service_name = NULL;
> +		llcp_sock->service_name_len = 0;
> +		llcp_sock->reserved_ssap = LLCP_SAP_MAX;
> +	}
> +

[Severity: High]
Can the same leak still happen through bind()?

After a DM(REJ/NOBOUND) rejects a nonblocking connect(), the socket is in
LLCP_CLOSED. It still holds a local reference, a reserved local SAP, the
kmemdup'd service_name and a dev reference.

llcp_sock_bind() accepts that state:

	if (sk->sk_state != LLCP_CLOSED) {
		ret = -EBADFD;
		goto error;
	}

It then overwrites llcp_sock->dev, ->local, ->service_name, ->ssap and
->reserved_ssap without releasing the old values. close() later releases
only what bind() took.

If connect(O_NONBLOCK) -> DM -> bind() -> close() is repeated about 32
times, wouldn't the local SAPs still run out, so that
nfc_llcp_get_local_ssap() fails for every client connect on the device?

Could this cleanup become a helper that llcp_sock_bind() also calls?

llcp_raw_sock_bind() has the same pattern of accepting CLOSED and
overwriting the fields. Raw sockets cannot reach this DM state, though,
because they use sock_no_connect.

[Severity: High]
This is a pre-existing issue, not one introduced by this patch. Still, the
retry path this patch is meant to support can oops the kernel.

nfc_llcp_recv_dm() sets sk->sk_err = ENXIO. Neither connect() nor this
new cleanup clears it. On a blocking retry, sock_wait_state() checks for a
pending error before it checks the state again:

	err = sock_error(sk);
	if (err)
		break;

Suppose the wakeup came from nfc_llcp_recv_cc(). The socket has already
moved to local->sockets and is set to LLCP_CONNECTED, but connect() sees
the stale -ENXIO and takes the sock_unlink path.

That path unlinks the socket from local->sockets while holding the
connecting_sockets lock. It frees the SAP, sets ->local and ->dev to NULL
and drops their references. It leaves sk_state at LLCP_CONNECTED.

On close(), llcp_sock_release() sees local == NULL and jumps to out. Then
llcp_sock_destruct() runs:

	if (sk->sk_state == LLCP_CONNECTED)
		nfc_put_device(llcp_sock->dev);

At this point llcp_sock->dev is NULL. Doesn't that make put_device()
dereference a near-NULL address?

Should the stale sk_err be cleared when reconnecting from LLCP_CLOSED?

[ ... ]

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260925184857.357926-1-qwe.aldo%40gmail.com

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-09-28  9:53 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-25 18:48 [PATCH net v2] nfc: llcp: prevent resource leak on repeated connect after DM Aldo Ariel Panzardo
2026-09-27 22:32 ` [syzbot ci] " syzbot ci
2026-09-28  3:57 ` [PATCH net v3] " Aldo Ariel Panzardo
2026-09-28  9:53 ` [PATCH net v2] " netdev-bot+sashiko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®