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