diff mbox series

[net] tcp: add sanity tests in tcp_add_backlog()

Message ID 20190426171005.172805-1-edumazet@google.com
State Accepted
Delegated to: David Miller
Headers show
Series [net] tcp: add sanity tests in tcp_add_backlog() | expand

Commit Message

Eric Dumazet April 26, 2019, 5:10 p.m. UTC
Richard and Bruno both reported that my commit added a bug,
and Bruno was able to determine the problem came when a segment
wih a FIN packet was coalesced to a prior one in tcp backlog queue.

It turns out the header prediction in tcp_rcv_established()
looks back to TCP headers in the packet, not in the metadata
(aka TCP_SKB_CB(skb)->tcp_flags)

The fast path in tcp_rcv_established() is not supposed to
handle a FIN flag (it does not call tcp_fin())

Therefore we need to make sure to propagate the FIN flag,
so that the coalesced packet does not go through the fast path,
the same than a GRO packet carrying a FIN flag.

While we are at it, make sure we do not coalesce packets with
RST or SYN, or if they do not have ACK set.

Many thanks to Richard and Bruno for pinpointing the bad commit,
and to Richard for providing a first version of the fix.

Fixes: 4f693b55c3d2 ("tcp: implement coalescing on backlog queue")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Reported-by: Bruno Prémont <bonbons@sysophe.eu>
---
 net/ipv4/tcp_ipv4.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

Comments

David Miller April 30, 2019, 3:21 a.m. UTC | #1
From: Eric Dumazet <edumazet@google.com>
Date: Fri, 26 Apr 2019 10:10:05 -0700

> Richard and Bruno both reported that my commit added a bug,
> and Bruno was able to determine the problem came when a segment
> wih a FIN packet was coalesced to a prior one in tcp backlog queue.
> 
> It turns out the header prediction in tcp_rcv_established()
> looks back to TCP headers in the packet, not in the metadata
> (aka TCP_SKB_CB(skb)->tcp_flags)
> 
> The fast path in tcp_rcv_established() is not supposed to
> handle a FIN flag (it does not call tcp_fin())
> 
> Therefore we need to make sure to propagate the FIN flag,
> so that the coalesced packet does not go through the fast path,
> the same than a GRO packet carrying a FIN flag.
> 
> While we are at it, make sure we do not coalesce packets with
> RST or SYN, or if they do not have ACK set.
> 
> Many thanks to Richard and Bruno for pinpointing the bad commit,
> and to Richard for providing a first version of the fix.
> 
> Fixes: 4f693b55c3d2 ("tcp: implement coalescing on backlog queue")
> Signed-off-by: Eric Dumazet <edumazet@google.com>
> Reported-by: Richard Purdie <richard.purdie@linuxfoundation.org>
> Reported-by: Bruno Prémont <bonbons@sysophe.eu>

Applied and queued up for -stable.
diff mbox series

Patch

diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 2f8039a26b08fa2b13b5e4da642c0f4ff8207571..a2896944aa377b7feef6417720348c02c3d8eecb 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1673,7 +1673,9 @@  bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
 	if (TCP_SKB_CB(tail)->end_seq != TCP_SKB_CB(skb)->seq ||
 	    TCP_SKB_CB(tail)->ip_dsfield != TCP_SKB_CB(skb)->ip_dsfield ||
 	    ((TCP_SKB_CB(tail)->tcp_flags |
-	      TCP_SKB_CB(skb)->tcp_flags) & TCPHDR_URG) ||
+	      TCP_SKB_CB(skb)->tcp_flags) & (TCPHDR_SYN | TCPHDR_RST | TCPHDR_URG)) ||
+	    !((TCP_SKB_CB(tail)->tcp_flags &
+	      TCP_SKB_CB(skb)->tcp_flags) & TCPHDR_ACK) ||
 	    ((TCP_SKB_CB(tail)->tcp_flags ^
 	      TCP_SKB_CB(skb)->tcp_flags) & (TCPHDR_ECE | TCPHDR_CWR)) ||
 #ifdef CONFIG_TLS_DEVICE
@@ -1692,6 +1694,15 @@  bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
 		if (after(TCP_SKB_CB(skb)->ack_seq, TCP_SKB_CB(tail)->ack_seq))
 			TCP_SKB_CB(tail)->ack_seq = TCP_SKB_CB(skb)->ack_seq;
 
+		/* We have to update both TCP_SKB_CB(tail)->tcp_flags and
+		 * thtail->fin, so that the fast path in tcp_rcv_established()
+		 * is not entered if we append a packet with a FIN.
+		 * SYN, RST, URG are not present.
+		 * ACK is set on both packets.
+		 * PSH : we do not really care in TCP stack,
+		 *       at least for 'GRO' packets.
+		 */
+		thtail->fin |= th->fin;
 		TCP_SKB_CB(tail)->tcp_flags |= TCP_SKB_CB(skb)->tcp_flags;
 
 		if (TCP_SKB_CB(skb)->has_rxtstamp) {