Skip to content

Commit

Permalink
tcp: fix splice() and tcp collapsing interaction
Browse files Browse the repository at this point in the history
Under unusual circumstances, TCP collapse can split a big GRO TCP packet
while its being used in a splice(socket->pipe) operation.

skb_splice_bits() releases the socket lock before calling
splice_to_pipe().

[ 1081.353685] WARNING: at net/ipv4/tcp.c:1330 tcp_cleanup_rbuf+0x4d/0xfc()
[ 1081.371956] Hardware name: System x3690 X5 -[7148Z68]-
[ 1081.391820] cleanup rbuf bug: copied AD3BCF1 seq AD370AF rcvnxt AD3CF13

To fix this problem, we must eat skbs in tcp_recv_skb().

Remove the inline keyword from tcp_recv_skb() definition since
it has three call sites.

Reported-by: Christian Becker <c.becker@traviangames.com>
Cc: Willy Tarreau <w@1wt.eu>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Tested-by: Willy Tarreau <w@1wt.eu>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Eric Dumazet authored and David S. Miller committed Jan 10, 2013
1 parent ff905b1 commit f26845b
Showing 1 changed file with 10 additions and 3 deletions.
13 changes: 10 additions & 3 deletions net/ipv4/tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1428,19 +1428,24 @@ static void tcp_service_net_dma(struct sock *sk, bool wait)
}
#endif

static inline struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off)
static struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off)
{
struct sk_buff *skb;
u32 offset;

skb_queue_walk(&sk->sk_receive_queue, skb) {
while ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) {
offset = seq - TCP_SKB_CB(skb)->seq;
if (tcp_hdr(skb)->syn)
offset--;
if (offset < skb->len || tcp_hdr(skb)->fin) {
*off = offset;
return skb;
}
/* This looks weird, but this can happen if TCP collapsing
* splitted a fat GRO packet, while we released socket lock
* in skb_splice_bits()
*/
sk_eat_skb(sk, skb, false);
}
return NULL;
}
Expand Down Expand Up @@ -1520,8 +1525,10 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
tcp_rcv_space_adjust(sk);

/* Clean up data we have read: This will do ACK frames. */
if (copied > 0)
if (copied > 0) {
tcp_recv_skb(sk, seq, &offset);
tcp_cleanup_rbuf(sk, copied);
}
return copied;
}
EXPORT_SYMBOL(tcp_read_sock);
Expand Down

0 comments on commit f26845b

Please sign in to comment.