RE: [PATCH] tipc: defer local Nagle backlog xmit from receive path
Tung Quang Nguyen <[email protected]>
| Newsgroups | dev.linux.lists.syzbot,org.kernel.vger.linux-kernel,org.kernel.vger.netdev |
|---|---|
| Message-ID | <GV1P189MB1988F6301FA670CA9818F7FCC6DB2@GV1P189MB1988.EURP189.PROD.OUTLOOK.COM> |
>Subject: [PATCH] tipc: defer local Nagle backlog xmit from receive path > >From: Bartosz Chronowski <[email protected]> > >A local TIPC stream socket workload can trap a CPU in an endless receive loop. >The resulting soft lockup can make the system unavailable. > >tipc_sk_push_backlog() can transmit delayed stream data while tipc_sk_rcv() >holds a destination socket's sk_lock.slock. Own-node transmission enters >tipc_sk_rcv() synchronously. A reply that reaches the ancestor socket cannot >acquire the still-held lock, and tipc_sk_rcv() retries without consuming its input >queue. > >Pass the receive output queue to tipc_sk_push_backlog() from receive-side >callers. Queue own-node output there so the enclosing receive path sends it >after releasing the socket lock. Keep shutdown and remote-node transmission >on the existing direct path, preserving remote link congestion handling. > >Fixes: c0bceb97db9e ("tipc: add smart nagle feature") >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=10a41dc44eef71aa9450 >Link: https://syzkaller.appspot.com/ai_job?id=aa2dc129-33d3-42e7-bd0b- >bf2198c9c9c2 >Signed-off-by: Bartosz Chronowski <[email protected]> > >--- >diff --git a/net/tipc/socket.c b/net/tipc/socket.c index e564341e0..33d744dc5 >100644 >--- a/net/tipc/socket.c >+++ b/net/tipc/socket.c >@@ -156,7 +156,8 @@ static int tipc_sk_insert(struct tipc_sock *tsk); static >void tipc_sk_remove(struct tipc_sock *tsk); static int __tipc_sendstream(struct >socket *sock, struct msghdr *m, size_t dsz); static int __tipc_sendmsg(struct >socket *sock, struct msghdr *m, size_t dsz); -static void >tipc_sk_push_backlog(struct tipc_sock *tsk, bool nagle_ack); >+static void tipc_sk_push_backlog(struct tipc_sock *tsk, bool nagle_ack, >+ struct sk_buff_head *xmitq); > static int tipc_wait_for_connect(struct socket *sock, long *timeo_p); > > static const struct proto_ops packet_ops; @@ -560,7 +561,7 @@ static void >__tipc_shutdown(struct socket *sock, int error) > !tsk_conn_cong(tsk))); > > /* Push out delayed messages if in Nagle mode */ >- tipc_sk_push_backlog(tsk, false); >+ tipc_sk_push_backlog(tsk, false, NULL); This is wrong because it breaks user applications (send()/close()) as well as current implementation. All pending messages in the socket queue need to be delivered before closing that socket. > /* Remove pending SYN */ > __skb_queue_purge(&sk->sk_write_queue); > >@@ -1267,8 +1268,10 @@ void tipc_sk_mcast_rcv(struct net *net, struct >sk_buff_head *arrvq, > > /* tipc_sk_push_backlog(): send accumulated buffers in socket write queue > * when socket is in Nagle mode >+ * @xmitq: receive output queue, or NULL outside receive context > */ >-static void tipc_sk_push_backlog(struct tipc_sock *tsk, bool nagle_ack) >+static void tipc_sk_push_backlog(struct tipc_sock *tsk, bool nagle_ack, >+ struct sk_buff_head *xmitq) > { > struct sk_buff_head *txq = &tsk->sk.sk_write_queue; > struct sk_buff *skb = skb_peek_tail(txq); @@ -1310,6 +1313,12 @@ >static void tipc_sk_push_backlog(struct tipc_sock *tsk, bool nagle_ack) > tsk->pkt_cnt += skb_queue_len(txq); > tsk->snt_unacked += tsk->snd_backlog; > tsk->snd_backlog = 0; >+ >+ if (xmitq && in_own_node(net, dnode)) { >+ skb_queue_splice_tail_init(txq, xmitq); >+ return; >+ } >+ > rc = tipc_node_xmit(net, txq, dnode, tsk->portid); > if (rc == -ELINKCONG) > tsk->cong_link_cnt = 1; >@@ -1367,7 +1376,7 @@ static void tipc_sk_conn_proto_rcv(struct tipc_sock >*tsk, struct sk_buff *skb, > goto exit; > > was_cong = tsk_conn_cong(tsk); >- tipc_sk_push_backlog(tsk, msg_nagle_ack(hdr)); >+ tipc_sk_push_backlog(tsk, msg_nagle_ack(hdr), xmitq); Passing a stack-based queue (local variable) to tipc_sk_push_backlog() is a wrong approach. This will break user applications because it will create out-of-order messages at receiver. ' tsk->sk.sk_write_queue' must always be sent under lock protection. After you copy its skbs to the local ' xmitq', there could be 2 threads sending ' tsk->sk.sk_write_queue' and 'xmitq'. This causes disordered messages. > tsk->snt_unacked -= msg_conn_ack(hdr); > if (tsk->peer_caps & TIPC_BLOCK_FLOWCTL) > tsk->snd_win = msg_adv_win(hdr); >@@ -2165,7 +2174,7 @@ static void tipc_sk_proto_rcv(struct sock *sk, > smp_wmb(); > tsk->cong_link_cnt--; > wakeup = true; >- tipc_sk_push_backlog(tsk, false); >+ tipc_sk_push_backlog(tsk, false, xmitq); > break; > case GROUP_PROTOCOL: > tipc_group_proto_rcv(grp, &wakeup, hdr, inputq, xmitq); @@ - >2256,7 +2265,7 @@ static bool tipc_sk_filter_connect(struct tipc_sock *tsk, >struct sk_buff *skb, > return false; > case TIPC_ESTABLISHED: > if (!skb_queue_empty(&sk->sk_write_queue)) >- tipc_sk_push_backlog(tsk, false); >+ tipc_sk_push_backlog(tsk, false, xmitq); > /* Accept only connection-based messages sent by peer */ > if (likely(con_msg && !err && pport == oport && > pnode == onode)) { > > >base-commit: 1590cf0329716306e948a8fc29f1d3ee87d3989f >-- >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].