lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20260128112348.3950437-1-dongchenchen2@huawei.com>
Date: Wed, 28 Jan 2026 19:23:48 +0800
From: Dong Chenchen <dongchenchen2@...wei.com>
To: <davem@...emloft.net>, <edumazet@...gle.com>, <kuba@...nel.org>,
	<pabeni@...hat.com>, <horms@...nel.org>, <andrew+netdev@...n.ch>,
	<sdf@...ichev.me>, <kuniyu@...gle.com>, <skhawaja@...gle.com>,
	<ahmed.zaki@...el.com>, <willemb@...gle.com>
CC: <netdev@...r.kernel.org>, <linux-kernel@...r.kernel.org>,
	<zhangchangzhong@...wei.com>, Dong Chenchen <dongchenchen2@...wei.com>
Subject: [BUG Report] BUG: soft lockup in ptype seq_file traversal

Hello,

syzkaller found a data race during concurrent traversal and modification
of packet_type list that can lead to soft lockups.

call trace as below:

watchdog: BUG: soft lockup - CPU#3 stuck for 35819s! [test:611]
Modules linked in:
CPU: 3 UID: 0 PID: 611 Comm: test Tainted: G -
L      6.19.0-rc4-00013-g7f98ab9da046 #2 NONE
Tainted: [L]=SOFTLOCKUP
Hardware name: linux,dummy-virt (DT)
pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : __asan_load8+0x4c/0xc0
lr : ptype_seq_next+0x3c/0x280
Call trace:
 __asan_load8+0x4c/0xc0 (P)
 ptype_seq_next+0x3c/0x280
 traverse.part.0+0xe8/0x250
 seq_read_iter+0x49c/0x780
 seq_read+0x190/0x210
 proc_reg_read+0x110/0x180
 do_loop_readv_writev.part.0+0x184/0x208
 vfs_readv+0x290/0x300
 __arm64_sys_preadv+0x15c/0x1c8
 invoke_syscall+0x68/0x190
 el0_svc_common.constprop.0+0x11c/0x150
 do_el0_svc+0x3c/0x58
 el0_svc+0x40/0xf8
 el0t_64_sync_handler+0xa0/0xe8
 el0t_64_sync+0x1ac/0x1b0

Root cause analysis:
The ptype_head maintains two lists: net->ptype_all and dev->ptype_all.
It determines whether the current pt belongs to the net->ptype_all or
dev->ptype_all list by examining the value of pt->dev. When pt->dev does
not correspond to the list that pt is currently in, the list head detection
will no longer work correctly.

The race condition occurs when ptype_seq_next() is traversing the list
while another thread concurrently modifies the list by removing/readding
packet_type entries.

CPU1				CPU2
ptype_seq_next
    nxt = pt->list.next;
    //nxt = ptype_head(pt) = dev->ptype_all
				packet_release/packet_notifier
				    unregister_prot_hook(sk, false);
				    //no sync wait, pt->list.next not change
				    po->prot_hook.dev = NULL;
    if (pt->dev) //check fail 
        if (nxt != &pt->dev->ptype_all)
            goto found;
    if (nxt != &ptype_all) //check success
        goto found;
found:
    return list_entry(nxt, struct packet_type, list);
    //return list head to seq traversal

reproduce flow:
fd=socket(AF_PACKET)
packet_bind(fd, dev)
			cat /proc/net/ptype //can add delay to ptype_seq_next
close(fd)

I have a preliminary fix as bellow[1], but the ptype file traversal may
block the psock release and dev unregister.
Does anyone have a good idea to solve this problem?

-----
Best Regards,
Dong Chenchen

[1] 
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 5870a9e514a5..ff92b8e8fc46 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2947,6 +2947,8 @@ struct packet_type {
        struct list_head        list;
 };
 
+extern rwlock_t ptype_lock;
+
 struct offload_callbacks {
        struct sk_buff          *(*gso_segment)(struct sk_buff *skb,
                                                netdev_features_t features);
diff --git a/net/core/dev.c b/net/core/dev.c
index 36dc5199037e..240702ac247e 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -168,7 +168,9 @@
 #include "devmem.h"
 #include "net-sysfs.h"
 
-static DEFINE_SPINLOCK(ptype_lock);
+DEFINE_RWLOCK(ptype_lock);
+EXPORT_SYMBOL(ptype_lock);
+
 struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
 
 static int netif_rx_internal(struct sk_buff *skb);
@@ -609,9 +611,9 @@ void dev_add_pack(struct packet_type *pt)
        if (WARN_ON_ONCE(!head))
                return;
 
-       spin_lock(&ptype_lock);
+       write_lock(&ptype_lock);
        list_add_rcu(&pt->list, head);
-       spin_unlock(&ptype_lock);
+       write_unlock(&ptype_lock);
 }
 EXPORT_SYMBOL(dev_add_pack);
 
@@ -636,7 +638,7 @@ void __dev_remove_pack(struct packet_type *pt)
        if (!head)
                return;
 
-       spin_lock(&ptype_lock);
+       write_lock(&ptype_lock);
 
        list_for_each_entry(pt1, head, list) {
                if (pt == pt1) {
@@ -647,7 +649,7 @@ void __dev_remove_pack(struct packet_type *pt)
 
        pr_warn("dev_remove_pack: %p not found\n", pt);
 out:
-       spin_unlock(&ptype_lock);
+       write_unlock(&ptype_lock);
 }
 EXPORT_SYMBOL(__dev_remove_pack);
 
diff --git a/net/core/net-procfs.c b/net/core/net-procfs.c
index 70e0e9a3b650..e6234f8e243f 100644
--- a/net/core/net-procfs.c
+++ b/net/core/net-procfs.c
@@ -213,6 +213,7 @@ static void *ptype_seq_start(struct seq_file *seq, loff_t *pos)
        __acquires(RCU)
 {
        rcu_read_lock();
+       read_lock(&ptype_lock);
        return *pos ? ptype_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
 }
 
@@ -274,6 +275,7 @@ static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 static void ptype_seq_stop(struct seq_file *seq, void *v)
        __releases(RCU)
 {
+       read_unlock(&ptype_lock);
        rcu_read_unlock();
 }

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ