* [syzbot] [net?] unregister_netdevice: waiting for DEV to become free (8)
@ 2023-06-10 1:34 syzbot
[not found] ` <20230621070710.380373-1-astrajoan@yahoo.com>
` (3 more replies)
0 siblings, 4 replies; 13+ messages in thread
From: syzbot @ 2023-06-10 1:34 UTC (permalink / raw)
To: arnd, bridge, davem, edumazet, kuba, linux-kernel, netdev,
nikolay, pabeni, roopa, syzkaller-bugs
Hello,
syzbot found the following issue on:
HEAD commit: 67faabbde36b selftests/bpf: Add missing prototypes for sev..
git tree: bpf-next
console+strace: https://syzkaller.appspot.com/x/log.txt?x=1381363b280000
kernel config: https://syzkaller.appspot.com/x/.config?x=5335204dcdecfda
dashboard link: https://syzkaller.appspot.com/bug?extid=881d65229ca4f9ae8c84
compiler: gcc (Debian 10.2.1-6) 10.2.1 20210110, GNU ld (GNU Binutils for Debian) 2.35.2
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=132faf93280000
C reproducer: https://syzkaller.appspot.com/x/repro.c?x=10532add280000
Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/751a0490d875/disk-67faabbd.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/2c5106cd9f1f/vmlinux-67faabbd.xz
kernel image: https://storage.googleapis.com/syzbot-assets/62c1154294e4/bzImage-67faabbd.xz
The issue was bisected to:
commit ad2f99aedf8fa77f3ae647153284fa63c43d3055
Author: Arnd Bergmann <arnd@arndb.de>
Date: Tue Jul 27 13:45:16 2021 +0000
net: bridge: move bridge ioctls out of .ndo_do_ioctl
bisection log: https://syzkaller.appspot.com/x/bisect.txt?x=146de6f1280000
final oops: https://syzkaller.appspot.com/x/report.txt?x=166de6f1280000
console output: https://syzkaller.appspot.com/x/log.txt?x=126de6f1280000
IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+881d65229ca4f9ae8c84@syzkaller.appspotmail.com
Fixes: ad2f99aedf8f ("net: bridge: move bridge ioctls out of .ndo_do_ioctl")
unregister_netdevice: waiting for bridge0 to become free. Usage count = 2
leaked reference.
__netdev_tracker_alloc include/linux/netdevice.h:4070 [inline]
netdev_hold include/linux/netdevice.h:4099 [inline]
dev_ifsioc+0xbc0/0xeb0 net/core/dev_ioctl.c:408
dev_ioctl+0x250/0x1090 net/core/dev_ioctl.c:605
sock_do_ioctl+0x15a/0x230 net/socket.c:1215
sock_ioctl+0x1f8/0x680 net/socket.c:1318
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+0x197/0x210 fs/ioctl.c:856
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x39/0xb0 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x63/0xcd
---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.
syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
For information about bisection process see: https://goo.gl/tpsmEJ#bisection
If the bug is already fixed, let syzbot know by replying with:
#syz fix: exact-commit-title
If you want syzbot to run the reproducer, reply with:
#syz test: git://repo/address.git branch-or-commit-hash
If you attach or paste a git patch, syzbot will apply it before testing.
If you want to change bug's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)
If the bug is a duplicate of another bug, reply with:
#syz dup: exact-subject-of-another-report
If you want to undo deduplication, reply with:
#syz undup
^ permalink raw reply [flat|nested] 13+ messages in thread[parent not found: <20230621070710.380373-1-astrajoan@yahoo.com>]
* Re: [syzbot] [net?] unregister_netdevice: waiting for DEV to become free (8)
[not found] ` <20230621070710.380373-1-astrajoan@yahoo.com>
@ 2023-06-21 8:46 ` Dongliang Mu
0 siblings, 0 replies; 13+ messages in thread
From: Dongliang Mu @ 2023-06-21 8:46 UTC (permalink / raw)
To: Ziqi Zhao
Cc: syzbot+881d65229ca4f9ae8c84, arnd, bridge, davem, edumazet, kuba,
linux-kernel, netdev, nikolay, pabeni, roopa, syzkaller-bugs,
skhan, ivan.orlov0322
On Wed, Jun 21, 2023 at 3:38 PM 'Ziqi Zhao' via syzkaller-bugs
<syzkaller-bugs@googlegroups.com> wrote:
>
> Hi all,
>
> I'm taking a look at this bug as part of the exercice for the Linux
> Kernel Bug Fixing Summer 2023 program. Thanks to the help from my
This is an interesting program. There are many kernel crashes on the
syzbot dashboard, which needs help.
> mentor, Ivan Orlov and Shuah Khan, I've already obtained a reproduction
> of the issue using the provided C reproducer, and I should be able to
> submit a patch by the end of this week to fix the highlighted error. If
> you have any information or suggestions, please feel free to reply to
> this thread. Any help would be greatly appreciated!
Please carefully read the guidance of submitting patches to linux
kernel [1]. Be careful about your coding style before sending.
Note that, Syzbot has the feature: patch testing. You can upload and
test your own patch to confirm that your patch is working properly.
[1] https://docs.kernel.org/process/submitting-patches.html
>
> Best regards,
> Ziqi
>
> --
> You received this message because you are subscribed to the Google Groups "syzkaller-bugs" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to syzkaller-bugs+unsubscribe@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/syzkaller-bugs/20230621070710.380373-1-astrajoan%40yahoo.com.
^ permalink raw reply [flat|nested] 13+ messages in thread
[parent not found: <20230819081057.330728-1-astrajoan@yahoo.com>]
* Re: [PATCH] net: bridge: Fix refcnt issues in dev_ioctl
[not found] ` <20230819081057.330728-1-astrajoan@yahoo.com>
@ 2023-08-19 9:25 ` Nikolay Aleksandrov
[not found] ` <20230819225048.dxxzv47fo64g24qx@Astras-Ubuntu>
0 siblings, 1 reply; 13+ messages in thread
From: Nikolay Aleksandrov @ 2023-08-19 9:25 UTC (permalink / raw)
To: Ziqi Zhao, arnd, bridge, davem, edumazet, f.fainelli,
ivan.orlov0322, keescook, kuba, hkallweit1, mudongliangabcd,
nikolay, pabeni, roopa, skhan, syzbot+881d65229ca4f9ae8c84,
vladimir.oltean
Cc: linux-kernel, netdev, syzkaller-bugs
Hi Ziqi,
On 8/19/23 11:10, Ziqi Zhao wrote:
> In the bug reported by Syzbot, certain bridge devices would have a
> leaked reference created by race conditions in dev_ioctl, specifically,
> under SIOCBRADDIF or SIOCBRDELIF operations. The reference leak would
How would it leak a reference, could you elaborate?
The reference is always taken and always released after the call.
> be shown in the periodic unregister_netdevice call, which throws a
> warning and cause Syzbot to report a crash. Upon inspection of the
If you reproduced it, is the device later removed or is it really stuck?
> logic in dev_ioctl, it seems the reference was introduced to ensure
> proper access to the bridge device after rtnl_unlock. and the latter
> function is necessary to maintain the following lock order in any
> bridge related ioctl calls:
>
> 1) br_ioctl_mutex => 2) rtnl_lock
>
> Conceptually, though, br_ioctl_mutex could be considered more specific
> than rtnl_lock given their usages, hence swapping their order would be
> a reasonable proposal. This patch changes all related call sites to
> maintain the reversed order of the two locks:
>
> 1) rtnl_lock => 2) br_ioctl_mutex
>
> By doing so, the extra reference introduced in dev_ioctl is no longer
> needed, and hence the reference leak bug is now resolved.
IIRC there was no bug, it was a false-positive. The reference is held a
bit longer but then released, so the device is deleted later.
I might be remembering wrong, but I think I briefly looked into this
when it was reported. If that's not the case I'd be interested to see
a new report/trace because the bug might be somewhere else.
>
> Reported-by: syzbot+881d65229ca4f9ae8c84@syzkaller.appspotmail.com
> Fixes: ad2f99aedf8f ("net: bridge: move bridge ioctls out of .ndo_do_ioctl")
> Signed-off-by: Ziqi Zhao <astrajoan@yahoo.com>
> ---
> net/bridge/br_ioctl.c | 4 ----
> net/core/dev_ioctl.c | 8 +-------
> net/socket.c | 2 ++
> 3 files changed, 3 insertions(+), 11 deletions(-)
>
Thanks,
Nik
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [syzbot] [net?] unregister_netdevice: waiting for DEV to become free (8)
2023-06-10 1:34 [syzbot] [net?] unregister_netdevice: waiting for DEV to become free (8) syzbot
[not found] ` <20230621070710.380373-1-astrajoan@yahoo.com>
[not found] ` <20230819081057.330728-1-astrajoan@yahoo.com>
@ 2025-08-25 12:35 ` Hillf Danton
2025-08-25 13:51 ` syzbot
2025-08-26 1:50 ` Hillf Danton
3 siblings, 1 reply; 13+ messages in thread
From: Hillf Danton @ 2025-08-25 12:35 UTC (permalink / raw)
To: syzbot; +Cc: linux-kernel, syzkaller-bugs
> Date: Fri, 09 Jun 2023 18:34:58 -0700 [thread overview]
> Hello,
>
> syzbot found the following issue on:
>
> HEAD commit: 67faabbde36b selftests/bpf: Add missing prototypes for sev..
> git tree: bpf-next
> console+strace: https://syzkaller.appspot.com/x/log.txt?x=1381363b280000
> kernel config: https://syzkaller.appspot.com/x/.config?x=5335204dcdecfda
> dashboard link: https://syzkaller.appspot.com/bug?extid=881d65229ca4f9ae8c84
> compiler: gcc (Debian 10.2.1-6) 10.2.1 20210110, GNU ld (GNU Binutils for Debian) 2.35.2
> syz repro: https://syzkaller.appspot.com/x/repro.syz?x=132faf93280000
> C reproducer: https://syzkaller.appspot.com/x/repro.c?x=10532add280000
#syz test upstream master
--- a/net/can/j1939/j1939-priv.h
+++ b/net/can/j1939/j1939-priv.h
@@ -59,6 +59,7 @@ struct j1939_priv {
/* segments need a lock to protect the above list */
rwlock_t lock;
+ int unregistered;
struct net_device *ndev;
/* list of 256 ecu ptrs, that cache the claimed addresses.
--- a/net/can/j1939/main.c
+++ b/net/can/j1939/main.c
@@ -157,13 +157,15 @@ static void __j1939_priv_release(struct
struct j1939_priv *priv = container_of(kref, struct j1939_priv, kref);
struct net_device *ndev = priv->ndev;
- netdev_dbg(priv->ndev, "%s: 0x%p\n", __func__, priv);
+ if (!priv->unregistered)
+ netdev_dbg(priv->ndev, "%s: 0x%p\n", __func__, priv);
WARN_ON_ONCE(!list_empty(&priv->active_session_list));
WARN_ON_ONCE(!list_empty(&priv->ecus));
WARN_ON_ONCE(!list_empty(&priv->j1939_socks));
- dev_put(ndev);
+ if (!priv->unregistered)
+ dev_put(ndev);
kfree(priv);
}
@@ -377,6 +379,10 @@ static int j1939_netdev_notify(struct no
j1939_sk_netdev_event_netdown(priv);
j1939_ecu_unmap_all(priv);
break;
+ case NETDEV_UNREGISTER:
+ priv->unregistered++;
+ dev_put(priv->ndev);
+ break;
}
j1939_priv_put(priv);
--
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [syzbot] [net?] unregister_netdevice: waiting for DEV to become free (8)
2025-08-25 12:35 ` [syzbot] [net?] unregister_netdevice: waiting for DEV to become free (8) Hillf Danton
@ 2025-08-25 13:51 ` syzbot
0 siblings, 0 replies; 13+ messages in thread
From: syzbot @ 2025-08-25 13:51 UTC (permalink / raw)
To: hdanton, linux-kernel, syzkaller-bugs
Hello,
syzbot has tested the proposed patch but the reproducer is still triggering an issue:
KASAN: use-after-free Read in j1939_netdev_stop
==================================================================
BUG: KASAN: use-after-free in read_pnet include/net/net_namespace.h:409 [inline]
BUG: KASAN: use-after-free in dev_net include/linux/netdevice.h:2718 [inline]
BUG: KASAN: use-after-free in j1939_can_rx_unregister net/can/j1939/main.c:202 [inline]
BUG: KASAN: use-after-free in __j1939_rx_release net/can/j1939/main.c:218 [inline]
BUG: KASAN: use-after-free in kref_put_mutex include/linux/kref.h:86 [inline]
BUG: KASAN: use-after-free in j1939_netdev_stop+0x2ab/0x2d0 net/can/j1939/main.c:311
Read of size 8 at addr ffff888023f80108 by task syz.0.17/6524
CPU: 0 UID: 0 PID: 6524 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/12/2025
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x116/0x1f0 lib/dump_stack.c:120
print_address_description mm/kasan/report.c:378 [inline]
print_report+0xcd/0x630 mm/kasan/report.c:482
kasan_report+0xe0/0x110 mm/kasan/report.c:595
read_pnet include/net/net_namespace.h:409 [inline]
dev_net include/linux/netdevice.h:2718 [inline]
j1939_can_rx_unregister net/can/j1939/main.c:202 [inline]
__j1939_rx_release net/can/j1939/main.c:218 [inline]
kref_put_mutex include/linux/kref.h:86 [inline]
j1939_netdev_stop+0x2ab/0x2d0 net/can/j1939/main.c:311
j1939_sk_release+0x5c3/0x8e0 net/can/j1939/socket.c:651
__sock_release+0xb3/0x270 net/socket.c:649
sock_close+0x1c/0x30 net/socket.c:1439
__fput+0x402/0xb70 fs/file_table.c:468
task_work_run+0x14d/0x240 kernel/task_work.c:227
exit_task_work include/linux/task_work.h:40 [inline]
do_exit+0x86f/0x2bf0 kernel/exit.c:961
do_group_exit+0xd3/0x2a0 kernel/exit.c:1102
get_signal+0x2673/0x26d0 kernel/signal.c:3034
arch_do_signal_or_restart+0x8f/0x7d0 arch/x86/kernel/signal.c:337
exit_to_user_mode_loop+0x84/0x110 kernel/entry/common.c:40
exit_to_user_mode_prepare include/linux/irq-entry-common.h:225 [inline]
syscall_exit_to_user_mode_work include/linux/entry-common.h:175 [inline]
syscall_exit_to_user_mode include/linux/entry-common.h:210 [inline]
do_syscall_64+0x3f6/0x4c0 arch/x86/entry/syscall_64.c:100
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f408338ebe9
Code: Unable to access opcode bytes at 0x7f408338ebbf.
RSP: 002b:00007f4084235038 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: 0000000000000024 RBX: 00007f40835b5fa0 RCX: 00007f408338ebe9
RDX: 0000000000000000 RSI: 0000200000000200 RDI: 0000000000000003
RBP: 00007f4083411e19 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f40835b6038 R14: 00007f40835b5fa0 R15: 00007ffd085a2ed8
</TASK>
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff888023f80000 pfn:0x23f80
flags: 0xfff00000000000(node=0|zone=1|lastcpupid=0x7ff)
raw: 00fff00000000000 ffffea000174f608 ffffea0000c2c208 0000000000000000
raw: ffff888023f80000 0000000000000000 00000000ffffffff 0000000000000000
page dumped because: kasan: bad access detected
page_owner tracks the page as freed
page last allocated via order 3, migratetype Unmovable, gfp_mask 0x446dc0(GFP_KERNEL_ACCOUNT|__GFP_ZERO|__GFP_NOWARN|__GFP_RETRY_MAYFAIL|__GFP_COMP), pid 6440, tgid 6440 (syz-executor), ts 122667010586, free_ts 125938931446
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1c0/0x230 mm/page_alloc.c:1851
prep_new_page mm/page_alloc.c:1859 [inline]
get_page_from_freelist+0x132b/0x38e0 mm/page_alloc.c:3858
__alloc_frozen_pages_noprof+0x261/0x23f0 mm/page_alloc.c:5148
alloc_pages_mpol+0x1fb/0x550 mm/mempolicy.c:2416
___kmalloc_large_node+0xed/0x160 mm/slub.c:4306
__kmalloc_large_node_noprof+0x1c/0x70 mm/slub.c:4337
__do_kmalloc_node mm/slub.c:4353 [inline]
__kvmalloc_node_noprof.cold+0xb/0x65 mm/slub.c:5052
alloc_netdev_mqs+0xd2/0x1530 net/core/dev.c:11812
rtnl_create_link+0xc08/0xf90 net/core/rtnetlink.c:3633
vxcan_newlink+0x2f8/0x640 drivers/net/can/vxcan.c:208
rtnl_newlink_create net/core/rtnetlink.c:3825 [inline]
__rtnl_newlink net/core/rtnetlink.c:3942 [inline]
rtnl_newlink+0xc45/0x2000 net/core/rtnetlink.c:4057
rtnetlink_rcv_msg+0x95e/0xe90 net/core/rtnetlink.c:6946
netlink_rcv_skb+0x158/0x420 net/netlink/af_netlink.c:2552
netlink_unicast_kernel net/netlink/af_netlink.c:1320 [inline]
netlink_unicast+0x5a7/0x870 net/netlink/af_netlink.c:1346
netlink_sendmsg+0x8d1/0xdd0 net/netlink/af_netlink.c:1896
sock_sendmsg_nosec net/socket.c:714 [inline]
__sock_sendmsg net/socket.c:729 [inline]
__sys_sendto+0x4a3/0x520 net/socket.c:2228
page last free pid 6524 tgid 6523 stack trace:
reset_page_owner include/linux/page_owner.h:25 [inline]
free_pages_prepare mm/page_alloc.c:1395 [inline]
__free_frozen_pages+0x7d5/0x10f0 mm/page_alloc.c:2895
device_release+0xa1/0x240 drivers/base/core.c:2565
kobject_cleanup lib/kobject.c:689 [inline]
kobject_release lib/kobject.c:720 [inline]
kref_put include/linux/kref.h:65 [inline]
kobject_put+0x1e7/0x5a0 lib/kobject.c:737
netdev_run_todo+0x7e9/0x1320 net/core/dev.c:11513
rtnl_unlock net/core/rtnetlink.c:157 [inline]
rtnl_net_unlock include/linux/rtnetlink.h:135 [inline]
rtnl_dellink+0x3da/0xa80 net/core/rtnetlink.c:3563
rtnetlink_rcv_msg+0x95e/0xe90 net/core/rtnetlink.c:6946
netlink_rcv_skb+0x158/0x420 net/netlink/af_netlink.c:2552
netlink_unicast_kernel net/netlink/af_netlink.c:1320 [inline]
netlink_unicast+0x5a7/0x870 net/netlink/af_netlink.c:1346
netlink_sendmsg+0x8d1/0xdd0 net/netlink/af_netlink.c:1896
sock_sendmsg_nosec net/socket.c:714 [inline]
__sock_sendmsg net/socket.c:729 [inline]
____sys_sendmsg+0xa95/0xc70 net/socket.c:2614
___sys_sendmsg+0x134/0x1d0 net/socket.c:2668
__sys_sendmsg+0x16d/0x220 net/socket.c:2700
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xcd/0x4c0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Memory state around the buggy address:
ffff888023f80000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ffff888023f80080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
>ffff888023f80100: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
^
ffff888023f80180: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ffff888023f80200: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
==================================================================
Tested on:
commit: 1b237f19 Linux 6.17-rc3
git tree: upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=10b66862580000
kernel config: https://syzkaller.appspot.com/x/.config?x=d4703ac89d9e185a
dashboard link: https://syzkaller.appspot.com/bug?extid=881d65229ca4f9ae8c84
compiler: gcc (Debian 12.2.0-14+deb12u1) 12.2.0, GNU ld (GNU Binutils for Debian) 2.40
patch: https://syzkaller.appspot.com/x/patch.diff?x=1417b862580000
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [syzbot] [net?] unregister_netdevice: waiting for DEV to become free (8)
2023-06-10 1:34 [syzbot] [net?] unregister_netdevice: waiting for DEV to become free (8) syzbot
` (2 preceding siblings ...)
2025-08-25 12:35 ` [syzbot] [net?] unregister_netdevice: waiting for DEV to become free (8) Hillf Danton
@ 2025-08-26 1:50 ` Hillf Danton
2025-08-26 2:48 ` syzbot
3 siblings, 1 reply; 13+ messages in thread
From: Hillf Danton @ 2025-08-26 1:50 UTC (permalink / raw)
To: syzbot; +Cc: linux-kernel, syzkaller-bugs
> Date: Fri, 09 Jun 2023 18:34:58 -0700 [thread overview]
> Hello,
>
> syzbot found the following issue on:
>
> HEAD commit: 67faabbde36b selftests/bpf: Add missing prototypes for sev..
> git tree: bpf-next
> console+strace: https://syzkaller.appspot.com/x/log.txt?x=1381363b280000
> kernel config: https://syzkaller.appspot.com/x/.config?x=5335204dcdecfda
> dashboard link: https://syzkaller.appspot.com/bug?extid=881d65229ca4f9ae8c84
> compiler: gcc (Debian 10.2.1-6) 10.2.1 20210110, GNU ld (GNU Binutils for Debian) 2.35.2
> syz repro: https://syzkaller.appspot.com/x/repro.syz?x=132faf93280000
> C reproducer: https://syzkaller.appspot.com/x/repro.c?x=10532add280000
#syz test upstream master
--- a/net/can/j1939/j1939-priv.h
+++ b/net/can/j1939/j1939-priv.h
@@ -59,6 +59,7 @@ struct j1939_priv {
/* segments need a lock to protect the above list */
rwlock_t lock;
+ int unregistered;
struct net_device *ndev;
/* list of 256 ecu ptrs, that cache the claimed addresses.
--- a/net/can/j1939/main.c
+++ b/net/can/j1939/main.c
@@ -157,13 +157,15 @@ static void __j1939_priv_release(struct
struct j1939_priv *priv = container_of(kref, struct j1939_priv, kref);
struct net_device *ndev = priv->ndev;
- netdev_dbg(priv->ndev, "%s: 0x%p\n", __func__, priv);
+ if (!priv->unregistered)
+ netdev_dbg(priv->ndev, "%s: 0x%p\n", __func__, priv);
WARN_ON_ONCE(!list_empty(&priv->active_session_list));
WARN_ON_ONCE(!list_empty(&priv->ecus));
WARN_ON_ONCE(!list_empty(&priv->j1939_socks));
- dev_put(ndev);
+ if (!priv->unregistered)
+ dev_put(ndev);
kfree(priv);
}
@@ -197,7 +199,8 @@ static void j1939_can_rx_unregister(stru
{
struct net_device *ndev = priv->ndev;
- can_rx_unregister(dev_net(ndev), ndev, J1939_CAN_ID, J1939_CAN_MASK,
+ if (!priv->unregistered)
+ can_rx_unregister(dev_net(ndev), ndev, J1939_CAN_ID, J1939_CAN_MASK,
j1939_can_recv, priv);
/* The last reference of priv is dropped by the RCU deferred
@@ -377,6 +380,10 @@ static int j1939_netdev_notify(struct no
j1939_sk_netdev_event_netdown(priv);
j1939_ecu_unmap_all(priv);
break;
+ case NETDEV_UNREGISTER:
+ priv->unregistered++;
+ dev_put(priv->ndev);
+ break;
}
j1939_priv_put(priv);
--
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [syzbot] [net?] unregister_netdevice: waiting for DEV to become free (8)
2025-08-26 1:50 ` Hillf Danton
@ 2025-08-26 2:48 ` syzbot
0 siblings, 0 replies; 13+ messages in thread
From: syzbot @ 2025-08-26 2:48 UTC (permalink / raw)
To: hdanton, linux-kernel, syzkaller-bugs
Hello,
syzbot has tested the proposed patch but the reproducer is still triggering an issue:
KASAN: use-after-free Read in j1939_netdev_stop
==================================================================
BUG: KASAN: use-after-free in netdev_get_ml_priv include/linux/netdevice.h:2692 [inline]
BUG: KASAN: use-after-free in can_get_ml_priv include/linux/can/can-ml.h:71 [inline]
BUG: KASAN: use-after-free in j1939_priv_set net/can/j1939/main.c:150 [inline]
BUG: KASAN: use-after-free in __j1939_rx_release net/can/j1939/main.c:221 [inline]
BUG: KASAN: use-after-free in kref_put_mutex include/linux/kref.h:86 [inline]
BUG: KASAN: use-after-free in j1939_netdev_stop+0x2df/0x320 net/can/j1939/main.c:312
Read of size 4 at addr ffff888062a506b0 by task syz.0.17/6467
CPU: 0 UID: 0 PID: 6467 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/12/2025
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x116/0x1f0 lib/dump_stack.c:120
print_address_description mm/kasan/report.c:378 [inline]
print_report+0xcd/0x630 mm/kasan/report.c:482
kasan_report+0xe0/0x110 mm/kasan/report.c:595
netdev_get_ml_priv include/linux/netdevice.h:2692 [inline]
can_get_ml_priv include/linux/can/can-ml.h:71 [inline]
j1939_priv_set net/can/j1939/main.c:150 [inline]
__j1939_rx_release net/can/j1939/main.c:221 [inline]
kref_put_mutex include/linux/kref.h:86 [inline]
j1939_netdev_stop+0x2df/0x320 net/can/j1939/main.c:312
j1939_sk_release+0x5c3/0x8e0 net/can/j1939/socket.c:651
__sock_release+0xb0/0x270 net/socket.c:649
sock_close+0x1c/0x30 net/socket.c:1439
__fput+0x402/0xb70 fs/file_table.c:468
task_work_run+0x14d/0x240 kernel/task_work.c:227
exit_task_work include/linux/task_work.h:40 [inline]
do_exit+0x86f/0x2bf0 kernel/exit.c:961
do_group_exit+0xd3/0x2a0 kernel/exit.c:1102
get_signal+0x2673/0x26d0 kernel/signal.c:3034
arch_do_signal_or_restart+0x8f/0x7d0 arch/x86/kernel/signal.c:337
exit_to_user_mode_loop+0x84/0x110 kernel/entry/common.c:40
exit_to_user_mode_prepare include/linux/irq-entry-common.h:225 [inline]
syscall_exit_to_user_mode_work include/linux/entry-common.h:175 [inline]
syscall_exit_to_user_mode include/linux/entry-common.h:210 [inline]
do_syscall_64+0x3f6/0x4c0 arch/x86/entry/syscall_64.c:100
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f15b7b8ebe9
Code: Unable to access opcode bytes at 0x7f15b7b8ebbf.
RSP: 002b:00007f15b899d038 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: 0000000000000024 RBX: 00007f15b7db5fa0 RCX: 00007f15b7b8ebe9
RDX: 0000000000000000 RSI: 0000200000000200 RDI: 0000000000000003
RBP: 00007f15b7c11e19 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f15b7db6038 R14: 00007f15b7db5fa0 R15: 00007ffec48e9178
</TASK>
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x62a50
flags: 0xfff00000000000(node=0|zone=1|lastcpupid=0x7ff)
raw: 00fff00000000000 ffffea0001db7008 ffffea00018a9208 0000000000000000
raw: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000
page dumped because: kasan: bad access detected
page_owner tracks the page as freed
page last allocated via order 3, migratetype Unmovable, gfp_mask 0x446dc0(GFP_KERNEL_ACCOUNT|__GFP_ZERO|__GFP_NOWARN|__GFP_RETRY_MAYFAIL|__GFP_COMP), pid 6357, tgid 6357 (syz-executor), ts 118494889601, free_ts 123112554125
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1c0/0x230 mm/page_alloc.c:1851
prep_new_page mm/page_alloc.c:1859 [inline]
get_page_from_freelist+0x132b/0x38e0 mm/page_alloc.c:3858
__alloc_frozen_pages_noprof+0x261/0x23f0 mm/page_alloc.c:5148
alloc_pages_mpol+0x1fb/0x550 mm/mempolicy.c:2416
___kmalloc_large_node+0xed/0x160 mm/slub.c:4306
__kmalloc_large_node_noprof+0x1c/0x70 mm/slub.c:4337
__do_kmalloc_node mm/slub.c:4353 [inline]
__kvmalloc_node_noprof.cold+0xb/0x65 mm/slub.c:5052
alloc_netdev_mqs+0xd2/0x1530 net/core/dev.c:11812
rtnl_create_link+0xc08/0xf90 net/core/rtnetlink.c:3633
vxcan_newlink+0x2f8/0x640 drivers/net/can/vxcan.c:208
rtnl_newlink_create net/core/rtnetlink.c:3825 [inline]
__rtnl_newlink net/core/rtnetlink.c:3942 [inline]
rtnl_newlink+0xc45/0x2000 net/core/rtnetlink.c:4057
rtnetlink_rcv_msg+0x95b/0xe90 net/core/rtnetlink.c:6946
netlink_rcv_skb+0x155/0x420 net/netlink/af_netlink.c:2552
netlink_unicast_kernel net/netlink/af_netlink.c:1320 [inline]
netlink_unicast+0x5aa/0x870 net/netlink/af_netlink.c:1346
netlink_sendmsg+0x8d1/0xdd0 net/netlink/af_netlink.c:1896
sock_sendmsg_nosec net/socket.c:714 [inline]
__sock_sendmsg net/socket.c:729 [inline]
__sys_sendto+0x4a0/0x520 net/socket.c:2228
page last free pid 6467 tgid 6466 stack trace:
reset_page_owner include/linux/page_owner.h:25 [inline]
free_pages_prepare mm/page_alloc.c:1395 [inline]
__free_frozen_pages+0x7d5/0x10f0 mm/page_alloc.c:2895
device_release+0xa1/0x240 drivers/base/core.c:2565
kobject_cleanup lib/kobject.c:689 [inline]
kobject_release lib/kobject.c:720 [inline]
kref_put include/linux/kref.h:65 [inline]
kobject_put+0x1e7/0x5a0 lib/kobject.c:737
netdev_run_todo+0x7e9/0x1320 net/core/dev.c:11513
rtnl_unlock net/core/rtnetlink.c:157 [inline]
rtnl_net_unlock include/linux/rtnetlink.h:135 [inline]
rtnl_dellink+0x3da/0xa80 net/core/rtnetlink.c:3563
rtnetlink_rcv_msg+0x95b/0xe90 net/core/rtnetlink.c:6946
netlink_rcv_skb+0x155/0x420 net/netlink/af_netlink.c:2552
netlink_unicast_kernel net/netlink/af_netlink.c:1320 [inline]
netlink_unicast+0x5aa/0x870 net/netlink/af_netlink.c:1346
netlink_sendmsg+0x8d1/0xdd0 net/netlink/af_netlink.c:1896
sock_sendmsg_nosec net/socket.c:714 [inline]
__sock_sendmsg net/socket.c:729 [inline]
____sys_sendmsg+0xa98/0xc70 net/socket.c:2614
___sys_sendmsg+0x134/0x1d0 net/socket.c:2668
__sys_sendmsg+0x16d/0x220 net/socket.c:2700
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xcd/0x4c0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Memory state around the buggy address:
ffff888062a50580: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ffff888062a50600: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
>ffff888062a50680: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
^
ffff888062a50700: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ffff888062a50780: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
==================================================================
Tested on:
commit: fab1beda Merge tag 'devicetree-fixes-for-6.17-1' of gi..
git tree: upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=16049c42580000
kernel config: https://syzkaller.appspot.com/x/.config?x=d4703ac89d9e185a
dashboard link: https://syzkaller.appspot.com/bug?extid=881d65229ca4f9ae8c84
compiler: gcc (Debian 12.2.0-14+deb12u1) 12.2.0, GNU ld (GNU Binutils for Debian) 2.40
patch: https://syzkaller.appspot.com/x/patch.diff?x=142de862580000
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] can: j1939: implement NETDEV_UNREGISTER notification handler
@ 2025-08-25 11:01 Tetsuo Handa
2025-08-25 13:35 ` [syzbot] [net?] unregister_netdevice: waiting for DEV to become free (8) syzbot
0 siblings, 1 reply; 13+ messages in thread
From: Tetsuo Handa @ 2025-08-25 11:01 UTC (permalink / raw)
To: syzbot+881d65229ca4f9ae8c84, LKML
syzbot is reporting
unregister_netdevice: waiting for vcan0 to become free. Usage count = 2
problem, for j1939 protocol did not have NETDEV_UNREGISTER notification
handler for undoing changes made by j1939_sk_bind().
Commit 25fe97cb7620 ("can: j1939: move j1939_priv_put() into sk_destruct
callback") expects that a call to j1939_priv_put() can be unconditionally
delayed until j1939_sk_sock_destruct() is called. But we need to call
j1939_priv_put() against an extra ref held by j1939_sk_bind() call
(as a part of undoing changes made by j1939_sk_bind()) as soon as
NETDEV_UNREGISTER notification fires (i.e. before j1939_sk_sock_destruct()
is called via j1939_sk_release()). Otherwise, the extra ref on "struct
j1939_priv" held by j1939_sk_bind() call prevents "struct net_device" from
dropping the usage count to 1; making it impossible for
unregister_netdevice() to continue.
Reported-by: syzbot <syzbot+881d65229ca4f9ae8c84@syzkaller.appspotmail.com>
Closes: https://syzkaller.appspot.com/bug?extid=881d65229ca4f9ae8c84
Fixes: 9d71dd0c7009 ("can: add support of SAE J1939 protocol")
Fixes: 25fe97cb7620 ("can: j1939: move j1939_priv_put() into sk_destruct callback")
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
#syz test
net/can/j1939/j1939-priv.h | 1 +
net/can/j1939/main.c | 3 +++
net/can/j1939/socket.c | 49 ++++++++++++++++++++++++++++++++++++++
3 files changed, 53 insertions(+)
diff --git a/net/can/j1939/j1939-priv.h b/net/can/j1939/j1939-priv.h
index 31a93cae5111..81f58924b4ac 100644
--- a/net/can/j1939/j1939-priv.h
+++ b/net/can/j1939/j1939-priv.h
@@ -212,6 +212,7 @@ void j1939_priv_get(struct j1939_priv *priv);
/* notify/alert all j1939 sockets bound to ifindex */
void j1939_sk_netdev_event_netdown(struct j1939_priv *priv);
+void j1939_sk_netdev_event_unregister(struct j1939_priv *priv);
int j1939_cancel_active_session(struct j1939_priv *priv, struct sock *sk);
void j1939_tp_init(struct j1939_priv *priv);
diff --git a/net/can/j1939/main.c b/net/can/j1939/main.c
index 7e8a20f2fc42..3706a872ecaf 100644
--- a/net/can/j1939/main.c
+++ b/net/can/j1939/main.c
@@ -377,6 +377,9 @@ static int j1939_netdev_notify(struct notifier_block *nb,
j1939_sk_netdev_event_netdown(priv);
j1939_ecu_unmap_all(priv);
break;
+ case NETDEV_UNREGISTER:
+ j1939_sk_netdev_event_unregister(priv);
+ break;
}
j1939_priv_put(priv);
diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c
index 493f49bfaf5d..72c649cec9e1 100644
--- a/net/can/j1939/socket.c
+++ b/net/can/j1939/socket.c
@@ -1303,6 +1303,55 @@ void j1939_sk_netdev_event_netdown(struct j1939_priv *priv)
read_unlock_bh(&priv->j1939_socks_lock);
}
+void j1939_sk_netdev_event_unregister(struct j1939_priv *priv)
+{
+ struct sock *sk;
+ struct j1939_sock *jsk;
+ bool wait_rcu = false;
+
+ rescan: /* The caller is holding a ref on this "priv" via j1939_priv_get_by_ndev(). */
+ read_lock_bh(&priv->j1939_socks_lock);
+ list_for_each_entry(jsk, &priv->j1939_socks, list) {
+ /* Skip if j1939_jsk_add() is not called on this socket. */
+ if (!(jsk->state & J1939_SOCK_BOUND))
+ continue;
+ sk = &jsk->sk;
+ sock_hold(sk);
+ read_unlock_bh(&priv->j1939_socks_lock);
+ /* Check if j1939_jsk_del() is not yet called on this socket after holding
+ * socket's lock, for both j1939_sk_bind() and j1939_sk_release() call
+ * j1939_jsk_del() with socket's lock held.
+ */
+ lock_sock(sk);
+ if (jsk->state & J1939_SOCK_BOUND) {
+ /* Neither j1939_sk_bind() nor j1939_sk_release() called j1939_jsk_del().
+ * Make this socket no longer bound, by pretending as if j1939_sk_bind()
+ * dropped old references but did not get new references.
+ */
+ j1939_jsk_del(priv, jsk);
+ j1939_local_ecu_put(priv, jsk->addr.src_name, jsk->addr.sa);
+ j1939_netdev_stop(priv);
+ /* Call j1939_priv_put() now and prevent j1939_sk_sock_destruct() from
+ * calling the corresponding j1939_priv_put().
+ *
+ * j1939_sk_sock_destruct() is supposed to call j1939_priv_put() after
+ * an RCU grace period. But since the caller is holding a ref on this
+ * "priv", we can defer synchronize_rcu() until immediately before
+ * the caller calls j1939_priv_put().
+ */
+ j1939_priv_put(priv);
+ jsk->priv = NULL;
+ wait_rcu = true;
+ }
+ release_sock(sk);
+ sock_put(sk);
+ goto rescan;
+ }
+ read_unlock_bh(&priv->j1939_socks_lock);
+ if (wait_rcu)
+ synchronize_rcu();
+}
+
static int j1939_sk_no_ioctlcmd(struct socket *sock, unsigned int cmd,
unsigned long arg)
{
--
2.51.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: unregister_netdevice: waiting for DEV to become free (8)
@ 2025-11-19 12:20 Tetsuo Handa
2025-11-19 13:09 ` [syzbot] [net?] " syzbot
0 siblings, 1 reply; 13+ messages in thread
From: Tetsuo Handa @ 2025-11-19 12:20 UTC (permalink / raw)
To: syzbot; +Cc: linux-kernel
#syz test: git://git.code.sf.net/p/tomoyo/tomoyo.git master
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: unregister_netdevice: waiting for DEV to become free (8)
2025-11-19 12:20 Tetsuo Handa
@ 2025-11-19 13:13 Tetsuo Handa
2025-11-19 13:57 ` [syzbot] [net?] " syzbot
0 siblings, 1 reply; 13+ messages in thread
From: Tetsuo Handa @ 2025-11-19 13:13 UTC (permalink / raw)
To: syzbot; +Cc: linux-kernel
Trying once more.
#syz test: git://git.code.sf.net/p/tomoyo/tomoyo.git master
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: unregister_netdevice: waiting for DEV to become free (8)
2025-11-19 13:13 Tetsuo Handa
@ 2025-11-19 14:00 Tetsuo Handa
2025-11-19 14:47 ` [syzbot] [net?] " syzbot
0 siblings, 1 reply; 13+ messages in thread
From: Tetsuo Handa @ 2025-11-19 14:00 UTC (permalink / raw)
To: syzbot; +Cc: linux-kernel
Too timing-dependent to trigger using a reproducer?
Or, a reproducer for an already-fixed bug is used?
#syz test
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index d1a687444b27..798d60b3e2ad 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2084,6 +2084,8 @@ enum netdev_reg_state {
*
* FIXME: cleanup struct net_device such that network protocol info
* moves out.
+ *
+ * @netdev_trace_buffer_list: Linked list for debugging refcount leak.
*/
struct net_device {
@@ -2238,6 +2240,9 @@ struct net_device {
#if IS_ENABLED(CONFIG_TLS_DEVICE)
const struct tlsdev_ops *tlsdev_ops;
#endif
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+ struct list_head netdev_trace_buffer_list;
+#endif
unsigned int operstate;
unsigned char link_mode;
@@ -3166,6 +3171,7 @@ enum netdev_cmd {
NETDEV_OFFLOAD_XSTATS_REPORT_USED,
NETDEV_OFFLOAD_XSTATS_REPORT_DELTA,
NETDEV_XDP_FEAT_CHANGE,
+ NETDEV_DEBUG_UNREGISTER,
};
const char *netdev_cmd_to_name(enum netdev_cmd cmd);
@@ -4345,9 +4351,15 @@ static inline bool dev_nit_active(const struct net_device *dev)
void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev);
+void save_netdev_trace_buffer(struct net_device *dev, int delta);
+int trim_netdev_trace(unsigned long *entries, int nr_entries);
+
static inline void __dev_put(struct net_device *dev)
{
if (dev) {
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+ save_netdev_trace_buffer(dev, -1);
+#endif
#ifdef CONFIG_PCPU_DEV_REFCNT
this_cpu_dec(*dev->pcpu_refcnt);
#else
@@ -4359,6 +4371,9 @@ static inline void __dev_put(struct net_device *dev)
static inline void __dev_hold(struct net_device *dev)
{
if (dev) {
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+ save_netdev_trace_buffer(dev, 1);
+#endif
#ifdef CONFIG_PCPU_DEV_REFCNT
this_cpu_inc(*dev->pcpu_refcnt);
#else
diff --git a/kernel/softirq.c b/kernel/softirq.c
index 77198911b8dd..5f435c1e48d8 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -576,6 +576,10 @@ static inline bool lockdep_softirq_start(void) { return false; }
static inline void lockdep_softirq_end(bool in_hardirq) { }
#endif
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+static noinline void handle_softirqs(bool ksirqd);
+#endif
+
static void handle_softirqs(bool ksirqd)
{
unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 45320e27a16c..e9c654a9d0bb 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -3145,6 +3145,10 @@ static bool manage_workers(struct worker *worker)
return true;
}
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+static noinline void process_one_work(struct worker *worker, struct work_struct *work);
+#endif
+
/**
* process_one_work - process single work
* @worker: self
diff --git a/net/can/j1939/main.c b/net/can/j1939/main.c
index a93af55df5fd..66e6624abfa3 100644
--- a/net/can/j1939/main.c
+++ b/net/can/j1939/main.c
@@ -124,6 +124,16 @@ static void j1939_can_recv(struct sk_buff *iskb, void *data)
static DEFINE_MUTEX(j1939_netdev_lock);
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+static void dump_priv_trace_buffer(const struct net_device *ndev);
+static void erase_priv_trace_buffer(struct j1939_priv *priv);
+static noinline void save_priv_trace_buffer(struct j1939_priv *priv, int delta);
+#else
+static inline void dump_priv_trace_buffer(const struct net_device *ndev) { };
+static inline void erase_priv_trace_buffer(struct j1939_priv *priv) { };
+static inline void save_priv_trace_buffer(struct j1939_priv *priv, int delta) { };
+#endif
+
static struct j1939_priv *j1939_priv_create(struct net_device *ndev)
{
struct j1939_priv *priv;
@@ -137,6 +147,7 @@ static struct j1939_priv *j1939_priv_create(struct net_device *ndev)
priv->ndev = ndev;
kref_init(&priv->kref);
kref_init(&priv->rx_kref);
+ save_priv_trace_buffer(priv, 1);
dev_hold(ndev);
netdev_dbg(priv->ndev, "%s : 0x%p\n", __func__, priv);
@@ -164,17 +175,20 @@ static void __j1939_priv_release(struct kref *kref)
WARN_ON_ONCE(!list_empty(&priv->j1939_socks));
dev_put(ndev);
+ erase_priv_trace_buffer(priv);
kfree(priv);
}
void j1939_priv_put(struct j1939_priv *priv)
{
+ save_priv_trace_buffer(priv, -1);
kref_put(&priv->kref, __j1939_priv_release);
}
void j1939_priv_get(struct j1939_priv *priv)
{
kref_get(&priv->kref);
+ save_priv_trace_buffer(priv, 1);
}
static int j1939_can_rx_register(struct j1939_priv *priv)
@@ -282,6 +296,7 @@ struct j1939_priv *j1939_netdev_start(struct net_device *ndev)
kref_get(&priv_new->rx_kref);
mutex_unlock(&j1939_netdev_lock);
dev_put(ndev);
+ erase_priv_trace_buffer(priv);
kfree(priv);
return priv_new;
}
@@ -299,6 +314,7 @@ struct j1939_priv *j1939_netdev_start(struct net_device *ndev)
mutex_unlock(&j1939_netdev_lock);
dev_put(ndev);
+ erase_priv_trace_buffer(priv);
kfree(priv);
return ERR_PTR(ret);
@@ -364,6 +380,9 @@ static int j1939_netdev_notify(struct notifier_block *nb,
struct can_ml_priv *can_ml = can_get_ml_priv(ndev);
struct j1939_priv *priv;
+ if (msg == NETDEV_DEBUG_UNREGISTER)
+ dump_priv_trace_buffer(ndev);
+
if (!can_ml)
goto notify_done;
@@ -428,3 +447,79 @@ static __exit void j1939_module_exit(void)
module_init(j1939_module_init);
module_exit(j1939_module_exit);
+
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+
+#define PRIV_TRACE_BUFFER_SIZE 1024
+static struct priv_trace_buffer {
+ struct j1939_priv *priv; // no-ref
+ struct net_device *ndev; // no-ref
+ atomic_t count;
+ int nr_entries;
+ unsigned long entries[20];
+} priv_trace_buffer[PRIV_TRACE_BUFFER_SIZE];
+static bool priv_trace_buffer_exhausted;
+
+static void dump_priv_trace_buffer(const struct net_device *ndev)
+{
+ struct priv_trace_buffer *ptr;
+ int count, balance = 0;
+ int i;
+
+ for (i = 0; i < PRIV_TRACE_BUFFER_SIZE; i++) {
+ ptr = &priv_trace_buffer[i];
+ if (!ptr->priv || ptr->ndev != ndev)
+ continue;
+ count = atomic_read(&ptr->count);
+ balance += count;
+ pr_info("Call trace for %s@%p %+d at\n", ndev->name, ptr->priv, count);
+ stack_trace_print(ptr->entries, ptr->nr_entries, 4);
+ }
+ if (!priv_trace_buffer_exhausted)
+ pr_info("balance for %s@j1939_priv is %d\n", ndev->name, balance);
+ else
+ pr_info("balance for %s@j1939_priv is unknown\n", ndev->name);
+}
+
+static void erase_priv_trace_buffer(struct j1939_priv *priv)
+{
+ int i;
+
+ for (i = 0; i < PRIV_TRACE_BUFFER_SIZE; i++)
+ if (priv_trace_buffer[i].priv == priv)
+ priv_trace_buffer[i].priv = NULL;
+}
+
+static noinline void save_priv_trace_buffer(struct j1939_priv *priv, int delta)
+{
+ struct priv_trace_buffer *ptr;
+ unsigned long entries[ARRAY_SIZE(ptr->entries)];
+ unsigned long nr_entries;
+ int i;
+
+ if (in_nmi())
+ return;
+ nr_entries = stack_trace_save(entries, ARRAY_SIZE(ptr->entries), 1);
+ nr_entries = trim_netdev_trace(entries, nr_entries);
+ for (i = 0; i < PRIV_TRACE_BUFFER_SIZE; i++) {
+ ptr = &priv_trace_buffer[i];
+ if (ptr->priv == priv && ptr->nr_entries == nr_entries &&
+ !memcmp(ptr->entries, entries, nr_entries * sizeof(unsigned long))) {
+ atomic_add(delta, &ptr->count);
+ return;
+ }
+ }
+ for (i = 0; i < PRIV_TRACE_BUFFER_SIZE; i++) {
+ ptr = &priv_trace_buffer[i];
+ if (!ptr->priv && !cmpxchg(&ptr->priv, NULL, priv)) {
+ ptr->ndev = priv->ndev;
+ atomic_set(&ptr->count, delta);
+ ptr->nr_entries = nr_entries;
+ memmove(ptr->entries, entries, nr_entries * sizeof(unsigned long));
+ return;
+ }
+ }
+ priv_trace_buffer_exhausted = true;
+}
+
+#endif
diff --git a/net/core/dev.c b/net/core/dev.c
index 2acfa44927da..c3a62c16fa15 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1854,6 +1854,7 @@ const char *netdev_cmd_to_name(enum netdev_cmd cmd)
N(PRE_CHANGEADDR) N(OFFLOAD_XSTATS_ENABLE) N(OFFLOAD_XSTATS_DISABLE)
N(OFFLOAD_XSTATS_REPORT_USED) N(OFFLOAD_XSTATS_REPORT_DELTA)
N(XDP_FEAT_CHANGE)
+ N(DEBUG_UNREGISTER)
}
#undef N
return "UNKNOWN_NETDEV_EVENT";
@@ -11429,6 +11430,14 @@ int netdev_refcnt_read(const struct net_device *dev)
}
EXPORT_SYMBOL(netdev_refcnt_read);
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+static void dump_netdev_trace_buffer(const struct net_device *dev);
+static void erase_netdev_trace_buffer(const struct net_device *dev);
+#else
+static inline void dump_netdev_trace_buffer(const struct net_device *dev) { }
+static inline void erase_netdev_trace_buffer(const struct net_device *dev) { }
+#endif
+
int netdev_unregister_timeout_secs __read_mostly = 10;
#define WAIT_REFS_MIN_MSECS 1
@@ -11502,11 +11511,16 @@ static struct net_device *netdev_wait_allrefs_any(struct list_head *list)
if (time_after(jiffies, warning_time +
READ_ONCE(netdev_unregister_timeout_secs) * HZ)) {
+ rtnl_lock();
list_for_each_entry(dev, list, todo_list) {
pr_emerg("unregister_netdevice: waiting for %s to become free. Usage count = %d\n",
dev->name, netdev_refcnt_read(dev));
ref_tracker_dir_print(&dev->refcnt_tracker, 10);
+ call_netdevice_notifiers(NETDEV_DEBUG_UNREGISTER, dev);
+ dump_netdev_trace_buffer(dev);
}
+ __rtnl_unlock();
+ rcu_barrier();
warning_time = jiffies;
}
@@ -11904,6 +11918,9 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
dev->priv_len = sizeof_priv;
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+ INIT_LIST_HEAD(&dev->netdev_trace_buffer_list);
+#endif
ref_tracker_dir_init(&dev->refcnt_tracker, 128, "netdev");
#ifdef CONFIG_PCPU_DEV_REFCNT
dev->pcpu_refcnt = alloc_percpu(int);
@@ -12076,6 +12093,8 @@ void free_netdev(struct net_device *dev)
mutex_destroy(&dev->lock);
+ erase_netdev_trace_buffer(dev);
+
/* Compatibility with error handling in drivers */
if (dev->reg_state == NETREG_UNINITIALIZED ||
dev->reg_state == NETREG_DUMMY) {
@@ -13090,3 +13109,180 @@ static int __init net_dev_init(void)
}
subsys_initcall(net_dev_init);
+
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+
+#define NETDEV_TRACE_BUFFER_SIZE 32768
+static struct netdev_trace_buffer {
+ struct list_head list;
+ int prev_count;
+ atomic_t count;
+ int nr_entries;
+ unsigned long entries[20];
+} netdev_trace_buffer[NETDEV_TRACE_BUFFER_SIZE];
+static LIST_HEAD(netdev_trace_buffer_list);
+static DEFINE_SPINLOCK(netdev_trace_buffer_lock);
+static bool netdev_trace_buffer_exhausted;
+
+static int netdev_trace_buffer_init(void)
+{
+ int i;
+
+ for (i = 0; i < NETDEV_TRACE_BUFFER_SIZE; i++)
+ list_add_tail(&netdev_trace_buffer[i].list, &netdev_trace_buffer_list);
+ return 0;
+}
+pure_initcall(netdev_trace_buffer_init);
+
+static void dump_netdev_trace_buffer(const struct net_device *dev)
+{
+ struct netdev_trace_buffer *ptr;
+ int count, balance = 0, pos = 0;
+
+ list_for_each_entry_rcu(ptr, &dev->netdev_trace_buffer_list, list,
+ /* list elements can't go away. */ 1) {
+ pos++;
+ count = atomic_read(&ptr->count);
+ balance += count;
+ if (ptr->prev_count == count)
+ continue;
+ ptr->prev_count = count;
+ pr_info("Call trace for %s[%d] %+d at\n", dev->name, pos, count);
+ stack_trace_print(ptr->entries, ptr->nr_entries, 4);
+ cond_resched();
+ }
+ if (!netdev_trace_buffer_exhausted)
+ pr_info("balance as of %s[%d] is %d\n", dev->name, pos, balance);
+}
+
+static void erase_netdev_trace_buffer(const struct net_device *dev)
+{
+ struct netdev_trace_buffer *ptr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&netdev_trace_buffer_lock, flags);
+ while (!list_empty(&dev->netdev_trace_buffer_list)) {
+ ptr = list_first_entry(&dev->netdev_trace_buffer_list, typeof(*ptr), list);
+ list_del(&ptr->list);
+ list_add_tail(&ptr->list, &netdev_trace_buffer_list);
+ }
+ spin_unlock_irqrestore(&netdev_trace_buffer_lock, flags);
+}
+
+#ifdef CONFIG_KALLSYMS
+static noinline unsigned long __find_trim(unsigned long *entries, int nr_entries, const char *name)
+{
+ int i;
+ char buffer[KSYM_SYMBOL_LEN];
+ const int len = strlen(name);
+
+ for (i = 0; i < nr_entries; i++) {
+ snprintf(buffer, sizeof(buffer), "%pS", (void *)entries[i]);
+ if (!strncmp(buffer, name, len) && buffer[len] == '+')
+ return entries[i];
+ }
+ return 0;
+}
+
+static unsigned long caller_handle_softirqs;
+static unsigned long caller_process_one_work;
+static unsigned long caller_ksys_unshare;
+static unsigned long caller___sys_bind;
+static unsigned long caller___sock_sendmsg;
+
+static int __init net_check_symbols(void)
+{
+ if (!kallsyms_lookup_name("handle_softirqs"))
+ caller_handle_softirqs = -1;
+ if (!kallsyms_lookup_name("process_one_work"))
+ caller_process_one_work = -1;
+ if (!kallsyms_lookup_name("ksys_unshare"))
+ caller_ksys_unshare = -1;
+ if (!kallsyms_lookup_name("__sys_bind"))
+ caller___sys_bind = -1;
+ if (!kallsyms_lookup_name("sock_sendmsg_nosec") &&
+ !kallsyms_lookup_name("__sock_sendmsg"))
+ caller___sock_sendmsg = -1;
+ return 0;
+}
+late_initcall(net_check_symbols);
+#endif
+
+int trim_netdev_trace(unsigned long *entries, int nr_entries)
+{
+#ifdef CONFIG_KALLSYMS
+ int i;
+
+ if (in_softirq()) {
+ if (unlikely(!caller_handle_softirqs))
+ caller_handle_softirqs = __find_trim(entries, nr_entries,
+ "handle_softirqs");
+ for (i = 0; i < nr_entries; i++)
+ if (entries[i] == caller_handle_softirqs)
+ return i + 1;
+ } else if (current->flags & PF_WQ_WORKER) {
+ if (unlikely(!caller_process_one_work))
+ caller_process_one_work = __find_trim(entries, nr_entries,
+ "process_one_work");
+ for (i = 0; i < nr_entries; i++)
+ if (entries[i] == caller_process_one_work)
+ return i + 1;
+ } else {
+ if (unlikely(!caller_ksys_unshare))
+ caller_ksys_unshare = __find_trim(entries, nr_entries, "ksys_unshare");
+ if (unlikely(!caller___sys_bind))
+ caller___sys_bind = __find_trim(entries, nr_entries, "__sys_bind");
+ if (unlikely(!caller___sock_sendmsg)) {
+ caller___sock_sendmsg = __find_trim(entries, nr_entries,
+ "sock_sendmsg_nosec");
+ if (!caller___sock_sendmsg)
+ caller___sock_sendmsg = __find_trim(entries, nr_entries,
+ "__sock_sendmsg");
+ }
+ for (i = 0; i < nr_entries; i++)
+ if (entries[i] == caller_ksys_unshare ||
+ entries[i] == caller___sys_bind ||
+ entries[i] == caller___sock_sendmsg)
+ return i + 1;
+ }
+#endif
+ return nr_entries;
+}
+EXPORT_SYMBOL(trim_netdev_trace);
+
+void save_netdev_trace_buffer(struct net_device *dev, int delta)
+{
+ struct netdev_trace_buffer *ptr;
+ unsigned long entries[ARRAY_SIZE(ptr->entries)];
+ unsigned long nr_entries;
+ unsigned long flags;
+
+ if (in_nmi())
+ return;
+ nr_entries = stack_trace_save(entries, ARRAY_SIZE(ptr->entries), 1);
+ nr_entries = trim_netdev_trace(entries, nr_entries);
+ list_for_each_entry_rcu(ptr, &dev->netdev_trace_buffer_list, list,
+ /* list elements can't go away. */ 1) {
+ if (ptr->nr_entries == nr_entries &&
+ !memcmp(ptr->entries, entries, nr_entries * sizeof(unsigned long))) {
+ atomic_add(delta, &ptr->count);
+ return;
+ }
+ }
+ spin_lock_irqsave(&netdev_trace_buffer_lock, flags);
+ if (!list_empty(&netdev_trace_buffer_list)) {
+ ptr = list_first_entry(&netdev_trace_buffer_list, typeof(*ptr), list);
+ list_del(&ptr->list);
+ ptr->prev_count = 0;
+ atomic_set(&ptr->count, delta);
+ ptr->nr_entries = nr_entries;
+ memmove(ptr->entries, entries, nr_entries * sizeof(unsigned long));
+ list_add_tail_rcu(&ptr->list, &dev->netdev_trace_buffer_list);
+ } else {
+ netdev_trace_buffer_exhausted = true;
+ }
+ spin_unlock_irqrestore(&netdev_trace_buffer_lock, flags);
+}
+EXPORT_SYMBOL(save_netdev_trace_buffer);
+
+#endif
diff --git a/net/core/lock_debug.c b/net/core/lock_debug.c
index 9e9fb25314b9..78d611bb6d1c 100644
--- a/net/core/lock_debug.c
+++ b/net/core/lock_debug.c
@@ -29,6 +29,7 @@ int netdev_debug_event(struct notifier_block *nb, unsigned long event,
case NETDEV_DOWN:
case NETDEV_REBOOT:
case NETDEV_UNREGISTER:
+ case NETDEV_DEBUG_UNREGISTER:
case NETDEV_CHANGEMTU:
case NETDEV_CHANGEADDR:
case NETDEV_PRE_CHANGEADDR:
diff --git a/net/socket.c b/net/socket.c
index e8892b218708..fce536d2d8b9 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -734,6 +734,10 @@ static inline int sock_sendmsg_nosec(struct socket *sock, struct msghdr *msg)
return ret;
}
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+static noinline int __sock_sendmsg(struct socket *sock, struct msghdr *msg);
+#endif
+
static int __sock_sendmsg(struct socket *sock, struct msghdr *msg)
{
int err = security_socket_sendmsg(sock, msg,
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: unregister_netdevice: waiting for DEV to become free (8)
2025-11-19 14:00 Tetsuo Handa
@ 2026-03-02 10:56 Tetsuo Handa
2026-03-02 11:21 ` [syzbot] [net?] " syzbot
0 siblings, 1 reply; 13+ messages in thread
From: Tetsuo Handa @ 2026-03-02 10:56 UTC (permalink / raw)
To: syzbot; +Cc: linux-kernel
#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index d4e6e00bb90a..97862830f7d0 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2102,6 +2102,8 @@ enum netdev_reg_state {
*
* FIXME: cleanup struct net_device such that network protocol info
* moves out.
+ *
+ * @netdev_trace_buffer_list: Linked list for debugging refcount leak.
*/
struct net_device {
@@ -2257,6 +2259,9 @@ struct net_device {
#if IS_ENABLED(CONFIG_TLS_DEVICE)
const struct tlsdev_ops *tlsdev_ops;
#endif
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+ struct list_head netdev_trace_buffer_list;
+#endif
unsigned int operstate;
unsigned char link_mode;
@@ -3185,6 +3190,7 @@ enum netdev_cmd {
NETDEV_OFFLOAD_XSTATS_REPORT_USED,
NETDEV_OFFLOAD_XSTATS_REPORT_DELTA,
NETDEV_XDP_FEAT_CHANGE,
+ NETDEV_DEBUG_UNREGISTER,
};
const char *netdev_cmd_to_name(enum netdev_cmd cmd);
@@ -4373,9 +4379,15 @@ static inline bool dev_nit_active(const struct net_device *dev)
void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev);
+void save_netdev_trace_buffer(struct net_device *dev, int delta);
+int trim_netdev_trace(unsigned long *entries, int nr_entries);
+
static inline void __dev_put(struct net_device *dev)
{
if (dev) {
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+ save_netdev_trace_buffer(dev, -1);
+#endif
#ifdef CONFIG_PCPU_DEV_REFCNT
this_cpu_dec(*dev->pcpu_refcnt);
#else
@@ -4387,6 +4399,9 @@ static inline void __dev_put(struct net_device *dev)
static inline void __dev_hold(struct net_device *dev)
{
if (dev) {
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+ save_netdev_trace_buffer(dev, 1);
+#endif
#ifdef CONFIG_PCPU_DEV_REFCNT
this_cpu_inc(*dev->pcpu_refcnt);
#else
diff --git a/kernel/softirq.c b/kernel/softirq.c
index 77198911b8dd..5f435c1e48d8 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -576,6 +576,10 @@ static inline bool lockdep_softirq_start(void) { return false; }
static inline void lockdep_softirq_end(bool in_hardirq) { }
#endif
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+static noinline void handle_softirqs(bool ksirqd);
+#endif
+
static void handle_softirqs(bool ksirqd)
{
unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index aeaec79bc09c..66cb4d8c00bb 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -3157,6 +3157,10 @@ static bool manage_workers(struct worker *worker)
return true;
}
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+static noinline void process_one_work(struct worker *worker, struct work_struct *work);
+#endif
+
/**
* process_one_work - process single work
* @worker: self
diff --git a/net/core/dev.c b/net/core/dev.c
index c1a9f7fdcffa..fbc205c82256 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1874,6 +1874,7 @@ const char *netdev_cmd_to_name(enum netdev_cmd cmd)
N(PRE_CHANGEADDR) N(OFFLOAD_XSTATS_ENABLE) N(OFFLOAD_XSTATS_DISABLE)
N(OFFLOAD_XSTATS_REPORT_USED) N(OFFLOAD_XSTATS_REPORT_DELTA)
N(XDP_FEAT_CHANGE)
+ N(DEBUG_UNREGISTER)
}
#undef N
return "UNKNOWN_NETDEV_EVENT";
@@ -11557,6 +11558,14 @@ int netdev_refcnt_read(const struct net_device *dev)
}
EXPORT_SYMBOL(netdev_refcnt_read);
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+static void dump_netdev_trace_buffer(const struct net_device *dev);
+static void erase_netdev_trace_buffer(const struct net_device *dev);
+#else
+static inline void dump_netdev_trace_buffer(const struct net_device *dev) { }
+static inline void erase_netdev_trace_buffer(const struct net_device *dev) { }
+#endif
+
int netdev_unregister_timeout_secs __read_mostly = 10;
#define WAIT_REFS_MIN_MSECS 1
@@ -11630,11 +11639,16 @@ static struct net_device *netdev_wait_allrefs_any(struct list_head *list)
if (time_after(jiffies, warning_time +
READ_ONCE(netdev_unregister_timeout_secs) * HZ)) {
+ rtnl_lock();
list_for_each_entry(dev, list, todo_list) {
pr_emerg("unregister_netdevice: waiting for %s to become free. Usage count = %d\n",
dev->name, netdev_refcnt_read(dev));
ref_tracker_dir_print(&dev->refcnt_tracker, 10);
+ call_netdevice_notifiers(NETDEV_DEBUG_UNREGISTER, dev);
+ dump_netdev_trace_buffer(dev);
}
+ __rtnl_unlock();
+ rcu_barrier();
warning_time = jiffies;
}
@@ -12032,6 +12046,9 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
dev->priv_len = sizeof_priv;
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+ INIT_LIST_HEAD(&dev->netdev_trace_buffer_list);
+#endif
ref_tracker_dir_init(&dev->refcnt_tracker, 128, "netdev");
#ifdef CONFIG_PCPU_DEV_REFCNT
dev->pcpu_refcnt = alloc_percpu(int);
@@ -12204,6 +12221,8 @@ void free_netdev(struct net_device *dev)
mutex_destroy(&dev->lock);
+ erase_netdev_trace_buffer(dev);
+
/* Compatibility with error handling in drivers */
if (dev->reg_state == NETREG_UNINITIALIZED ||
dev->reg_state == NETREG_DUMMY) {
@@ -13306,3 +13325,171 @@ static int __init net_dev_init(void)
}
subsys_initcall(net_dev_init);
+
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+
+#define NETDEV_TRACE_BUFFER_SIZE 32768
+static struct netdev_trace_buffer {
+ struct list_head list;
+ int prev_count;
+ atomic_t count;
+ int nr_entries;
+ unsigned long entries[20];
+} netdev_trace_buffer[NETDEV_TRACE_BUFFER_SIZE];
+static LIST_HEAD(netdev_trace_buffer_list);
+static DEFINE_SPINLOCK(netdev_trace_buffer_lock);
+static bool netdev_trace_buffer_exhausted;
+
+static int netdev_trace_buffer_init(void)
+{
+ int i;
+
+ for (i = 0; i < NETDEV_TRACE_BUFFER_SIZE; i++)
+ list_add_tail(&netdev_trace_buffer[i].list, &netdev_trace_buffer_list);
+ return 0;
+}
+pure_initcall(netdev_trace_buffer_init);
+
+static void dump_netdev_trace_buffer(const struct net_device *dev)
+{
+ struct netdev_trace_buffer *ptr;
+ int count, balance = 0, pos = 0;
+
+ list_for_each_entry_rcu(ptr, &dev->netdev_trace_buffer_list, list,
+ /* list elements can't go away. */ 1) {
+ pos++;
+ count = atomic_read(&ptr->count);
+ balance += count;
+ if (ptr->prev_count == count)
+ continue;
+ ptr->prev_count = count;
+ pr_info("Call trace for %s[%d] %+d at\n", dev->name, pos, count);
+ stack_trace_print(ptr->entries, ptr->nr_entries, 4);
+ cond_resched();
+ }
+ if (!netdev_trace_buffer_exhausted)
+ pr_info("balance as of %s[%d] is %d\n", dev->name, pos, balance);
+}
+
+static void erase_netdev_trace_buffer(const struct net_device *dev)
+{
+ struct netdev_trace_buffer *ptr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&netdev_trace_buffer_lock, flags);
+ while (!list_empty(&dev->netdev_trace_buffer_list)) {
+ ptr = list_first_entry(&dev->netdev_trace_buffer_list, typeof(*ptr), list);
+ list_del(&ptr->list);
+ list_add_tail(&ptr->list, &netdev_trace_buffer_list);
+ }
+ spin_unlock_irqrestore(&netdev_trace_buffer_lock, flags);
+}
+
+int trim_netdev_trace(unsigned long *entries, int nr_entries)
+{
+#ifdef CONFIG_KALLSYMS
+ char buffer[32] = { };
+ char *cp;
+ int i;
+
+ if (in_softirq()) {
+ static unsigned long __data_racy caller;
+
+ if (!caller) {
+ for (i = 0; i < nr_entries; i++) {
+ snprintf(buffer, sizeof(buffer) - 1, "%ps", (void *)entries[i]);
+ cp = strchr(buffer, ' ');
+ if (cp)
+ *cp = '\0';
+ if (!strcmp(buffer, "handle_softirqs")) {
+ caller = entries[i];
+ break;
+ }
+ }
+ }
+ for (i = 0; i < nr_entries; i++)
+ if (entries[i] == caller)
+ return i + 1;
+ } else if (current->flags & PF_WQ_WORKER) {
+ static unsigned long __data_racy caller;
+
+ if (!caller) {
+ for (i = 0; i < nr_entries; i++) {
+ snprintf(buffer, sizeof(buffer) - 1, "%ps", (void *)entries[i]);
+ cp = strchr(buffer, ' ');
+ if (cp)
+ *cp = '\0';
+ if (!strcmp(buffer, "process_one_work")) {
+ caller = entries[i];
+ break;
+ }
+ }
+ }
+ for (i = 0; i < nr_entries; i++)
+ if (entries[i] == caller)
+ return i + 1;
+ } else {
+ for (i = 0; i < nr_entries; i++) {
+ snprintf(buffer, sizeof(buffer) - 1, "%ps", (void *)entries[i]);
+ cp = strchr(buffer, ' ');
+ if (cp)
+ *cp = '\0';
+ if (buffer[0] == 'k') {
+ if (!strcmp(buffer, "ksys_unshare"))
+ return i + 1;
+ } else if (buffer[0] == 's') {
+ if (!strcmp(buffer, "sock_sendmsg_nosec") ||
+ !strcmp(buffer, "sock_recvmsg_nosec"))
+ return i + 1;
+ } else if (buffer[0] == '_') {
+ if (!strcmp(buffer, "__sys_bind") ||
+ !strcmp(buffer, "__sock_release") ||
+ !strcmp(buffer, "__sys_bpf"))
+ return i + 1;
+ } else {
+ if (!strcmp(buffer, "do_sock_setsockopt"))
+ return i + 1;
+ }
+ }
+ }
+#endif
+ return nr_entries;
+}
+EXPORT_SYMBOL(trim_netdev_trace);
+
+void save_netdev_trace_buffer(struct net_device *dev, int delta)
+{
+ struct netdev_trace_buffer *ptr;
+ unsigned long entries[ARRAY_SIZE(ptr->entries)];
+ unsigned long nr_entries;
+ unsigned long flags;
+
+ if (in_nmi())
+ return;
+ nr_entries = stack_trace_save(entries, ARRAY_SIZE(ptr->entries), 1);
+ nr_entries = trim_netdev_trace(entries, nr_entries);
+ list_for_each_entry_rcu(ptr, &dev->netdev_trace_buffer_list, list,
+ /* list elements can't go away. */ 1) {
+ if (ptr->nr_entries == nr_entries &&
+ !memcmp(ptr->entries, entries, nr_entries * sizeof(unsigned long))) {
+ atomic_add(delta, &ptr->count);
+ return;
+ }
+ }
+ spin_lock_irqsave(&netdev_trace_buffer_lock, flags);
+ if (!list_empty(&netdev_trace_buffer_list)) {
+ ptr = list_first_entry(&netdev_trace_buffer_list, typeof(*ptr), list);
+ list_del(&ptr->list);
+ ptr->prev_count = 0;
+ atomic_set(&ptr->count, delta);
+ ptr->nr_entries = nr_entries;
+ memmove(ptr->entries, entries, nr_entries * sizeof(unsigned long));
+ list_add_tail_rcu(&ptr->list, &dev->netdev_trace_buffer_list);
+ } else {
+ netdev_trace_buffer_exhausted = true;
+ }
+ spin_unlock_irqrestore(&netdev_trace_buffer_lock, flags);
+}
+EXPORT_SYMBOL(save_netdev_trace_buffer);
+
+#endif
diff --git a/net/core/lock_debug.c b/net/core/lock_debug.c
index 9e9fb25314b9..78d611bb6d1c 100644
--- a/net/core/lock_debug.c
+++ b/net/core/lock_debug.c
@@ -29,6 +29,7 @@ int netdev_debug_event(struct notifier_block *nb, unsigned long event,
case NETDEV_DOWN:
case NETDEV_REBOOT:
case NETDEV_UNREGISTER:
+ case NETDEV_DEBUG_UNREGISTER:
case NETDEV_CHANGEMTU:
case NETDEV_CHANGEADDR:
case NETDEV_PRE_CHANGEADDR:
diff --git a/net/socket.c b/net/socket.c
index 05952188127f..53c4b1fd3ef7 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -650,7 +650,11 @@ struct socket *sock_alloc(void)
}
EXPORT_SYMBOL(sock_alloc);
-static void __sock_release(struct socket *sock, struct inode *inode)
+static
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+noinline
+#endif
+void __sock_release(struct socket *sock, struct inode *inode)
{
const struct proto_ops *ops = READ_ONCE(sock->ops);
@@ -722,7 +726,13 @@ static noinline void call_trace_sock_send_length(struct sock *sk, int ret,
trace_sock_send_length(sk, ret, 0);
}
-static inline int sock_sendmsg_nosec(struct socket *sock, struct msghdr *msg)
+static
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+noinline
+#else
+inline
+#endif
+int sock_sendmsg_nosec(struct socket *sock, struct msghdr *msg)
{
int ret = INDIRECT_CALL_INET(READ_ONCE(sock->ops)->sendmsg, inet6_sendmsg,
inet_sendmsg, sock, msg,
@@ -1072,8 +1082,13 @@ static noinline void call_trace_sock_recv_length(struct sock *sk, int ret, int f
trace_sock_recv_length(sk, ret, flags);
}
-static inline int sock_recvmsg_nosec(struct socket *sock, struct msghdr *msg,
- int flags)
+static
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+noinline
+#else
+inline
+#endif
+int sock_recvmsg_nosec(struct socket *sock, struct msghdr *msg, int flags)
{
int ret = INDIRECT_CALL_INET(READ_ONCE(sock->ops)->recvmsg,
inet6_recvmsg,
@@ -2532,9 +2547,12 @@ static int copy_msghdr_from_user(struct msghdr *kmsg,
return err < 0 ? err : 0;
}
-static int ____sys_sendmsg(struct socket *sock, struct msghdr *msg_sys,
- unsigned int flags, struct used_address *used_address,
- unsigned int allowed_msghdr_flags)
+static
+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
+noinline
+#endif
+int ____sys_sendmsg(struct socket *sock, struct msghdr *msg_sys, unsigned int flags,
+ struct used_address *used_address, unsigned int allowed_msghdr_flags)
{
unsigned char ctl[sizeof(struct cmsghdr) + 20]
__aligned(sizeof(__kernel_size_t));
^ permalink raw reply related [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-03-02 11:21 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-06-10 1:34 [syzbot] [net?] unregister_netdevice: waiting for DEV to become free (8) syzbot
[not found] ` <20230621070710.380373-1-astrajoan@yahoo.com>
2023-06-21 8:46 ` Dongliang Mu
[not found] ` <20230819081057.330728-1-astrajoan@yahoo.com>
2023-08-19 9:25 ` [PATCH] net: bridge: Fix refcnt issues in dev_ioctl Nikolay Aleksandrov
[not found] ` <20230819225048.dxxzv47fo64g24qx@Astras-Ubuntu>
2023-08-22 10:40 ` Nikolay Aleksandrov
2025-08-25 12:35 ` [syzbot] [net?] unregister_netdevice: waiting for DEV to become free (8) Hillf Danton
2025-08-25 13:51 ` syzbot
2025-08-26 1:50 ` Hillf Danton
2025-08-26 2:48 ` syzbot
-- strict thread matches above, loose matches on Subject: below --
2025-08-25 11:01 [PATCH] can: j1939: implement NETDEV_UNREGISTER notification handler Tetsuo Handa
2025-08-25 13:35 ` [syzbot] [net?] unregister_netdevice: waiting for DEV to become free (8) syzbot
2025-11-19 12:20 Tetsuo Handa
2025-11-19 13:09 ` [syzbot] [net?] " syzbot
2025-11-19 13:13 Tetsuo Handa
2025-11-19 13:57 ` [syzbot] [net?] " syzbot
2025-11-19 14:00 Tetsuo Handa
2025-11-19 14:47 ` [syzbot] [net?] " syzbot
2026-03-02 10:56 Tetsuo Handa
2026-03-02 11:21 ` [syzbot] [net?] " syzbot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox