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