[<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