[PATCH RFC] net/x25: fix sleeping function called from invalid context

"syzbot" <[email protected]>
Newsgroups dev.linux.lists.syzbot
Message-ID <[email protected]>
Commit 7781607938c8 ("net/x25: Fix null-ptr-deref caused by
x25_disconnect") added a lock_sock() call to x25_kill_by_neigh() to prevent
a race condition where x25_disconnect() clears x25->neighbour concurrently
while x25_sendmsg() or x25_recvmsg() are dereferencing it. However,
x25_kill_by_neigh() is called from softirq context (e.g., via
net_rx_action() -> x25_lapb_receive_frame() -> x25_link_terminated()),
where sleeping is strictly forbidden. Acquiring a sleeping lock in this
context triggers a "sleeping function called from invalid context" BUG and
an inconsistent lock state warning.

BUG: sleeping function called from invalid context at net/core/sock.c:3829
in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 24, name:
kworker/1:0
preempt_count: 101, expected: 0
RCU nest depth: 1, expected: 0
Call Trace:
 <IRQ>
 dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
 __might_resched+0x378/0x4d0 kernel/sched/core.c:9197
 lock_sock_nested+0x56/0x100 net/core/sock.c:3829
 lock_sock include/net/sock.h:1713 [inline]
 x25_kill_by_neigh+0x10d/0x250 net/x25/af_x25.c:1778
 x25_lapb_receive_frame+0x1b0/0xfb0 net/x25/x25_dev.c:138
 __netif_receive_skb_one_core net/core/dev.c:6212 [inline]
 netif_receive_skb_core+0x2a6/0x2b0 net/core/dev.c:6237
 lapbeth_napi_poll+0x42/0x90 drivers/net/wan/lapbether.c:100
 __napi_poll+0xaa/0x330 net/core/dev.c:7735
 napi_poll net/core/dev.c:7798 [inline]
 net_rx_action+0x61d/0xf50 net/core/dev.c:7955
 handle_softirqs+0x225/0x840 kernel/softirq.c:622
 do_softirq+0x76/0xd0 kernel/softirq.c:523
 </IRQ>

To fix this, defer the disconnect operation to a process context using a
work_struct. This approach safely synchronizes the disconnect operation
with user-space socket operations without sleeping in softirq, preserving
the original intent of the null-ptr-deref fix. As a bonus, this improves
the performance of x25_kill_by_neigh() from O(N^2) to O(N) by eliminating
the need to drop the list lock and restart the iteration.

Fixes: 7781607938c8 ("net/x25: Fix null-ptr-deref caused by x25_disconnect")
Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot
Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=15cbd86f569e8b084383
Link: https://syzkaller.appspot.com/ai_job?id=52927852-8f6a-44c7-84a5-8eca5340fc99
To: "David S. Miller" <[email protected]>
To: "Eric Dumazet" <[email protected]>
To: "Jakub Kicinski" <[email protected]>
To: <[email protected]>
To: "Martin Schiller" <[email protected]>
To: <[email protected]>
To: "Paolo Abeni" <[email protected]>
To: "Duoming Zhou" <[email protected]>
Cc: "Simon Horman" <[email protected]>
Cc: <[email protected]>

---
diff --git a/include/net/x25.h b/include/net/x25.h
index 414f3fd99..c83d4e2e6 100644
--- a/include/net/x25.h
+++ b/include/net/x25.h
@@ -167,6 +167,7 @@ struct x25_sock {
 	struct x25_dte_facilities dte_facilities;
 	struct x25_calluserdata	calluserdata;
 	unsigned long 		vc_facil_mask;	/* inc_call facilities mask */
+	struct work_struct	disconnect_work;
 };
 
 struct x25_forward {
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
index 033e7d059..7204ad352 100644
--- a/net/x25/af_x25.c
+++ b/net/x25/af_x25.c
@@ -501,6 +501,18 @@ static struct proto x25_proto = {
 	.obj_size = sizeof(struct x25_sock),
 };
 
+static void x25_disconnect_work(struct work_struct *work)
+{
+	struct x25_sock *x25 = container_of(work, struct x25_sock, disconnect_work);
+	struct sock *sk = &x25->sk;
+
+	lock_sock(sk);
+	if (x25->neighbour)
+		x25_disconnect(sk, ENETUNREACH, 0, 0);
+	release_sock(sk);
+	sock_put(sk);
+}
+
 static struct sock *x25_alloc_socket(struct net *net, int kern)
 {
 	struct x25_sock *x25;
@@ -516,6 +528,7 @@ static struct sock *x25_alloc_socket(struct net *net, int kern)
 	skb_queue_head_init(&x25->fragment_queue);
 	skb_queue_head_init(&x25->interrupt_in_queue);
 	skb_queue_head_init(&x25->interrupt_out_queue);
+	INIT_WORK(&x25->disconnect_work, x25_disconnect_work);
 out:
 	return sk;
 }
@@ -1768,19 +1781,13 @@ void x25_kill_by_neigh(struct x25_neigh *nb)
 {
 	struct sock *s;
 
-again:
 	write_lock_bh(&x25_list_lock);
 
 	sk_for_each(s, &x25_list) {
 		if (x25_sk(s)->neighbour == nb) {
 			sock_hold(s);
-			write_unlock_bh(&x25_list_lock);
-			lock_sock(s);
-			if (x25_sk(s)->neighbour == nb)
-				x25_disconnect(s, ENETUNREACH, 0, 0);
-			release_sock(s);
-			sock_put(s);
-			goto again;
+			if (!schedule_work(&x25_sk(s)->disconnect_work))
+				sock_put(s);
 		}
 	}
 	write_unlock_bh(&x25_list_lock);


base-commit: db2ddb87143519e20a95aa36c60b36107b736a58
-- 
This is an AI-generated patch subject to moderation.
Reply with '#syz upstream' to Sign-off the patch as a human author
and send it to the upstream kernel mailing lists.
Reply with '#syz reject' to reject it ('#syz unreject' to undo).

See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
The person who has signed off on the patch is responsible for
addressing comments.
syzbot engineers can be reached at [email protected].
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.