RE: tainted warnings with tcp splicing in 3.7.1

Lukas Tribus
2013-01-10T18:33:51Z

Hi Eric,

this is probably a dumb question ... but since the fix is in net/ipv4/tcp.c I was asking myself if this can affect IPv6 as well?

Thanks,

Lukas

> [PATCH] tcp: fix splice() and tcp collapsing interaction
>
> 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>
> ---
> net/ipv4/tcp.c | 13 ++++++++++---
> 1 file changed, 10 insertions(+), 3 deletions(-)
>
> diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
> index 1ca2536..1f901be 100644
> --- a/net/ipv4/tcp.c
> +++ b/net/ipv4/tcp.c
> @@ -1428,12 +1428,12 @@ 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--;
> @@ -1441,6 +1441,11 @@ static inline struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off)
> *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;
> }
> @@ -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);
>