Skip to content

Commit 9b7177b

Browse files
jrfastabborkmann
authored andcommitted
bpf: tcp_read_skb needs to pop skb regardless of seq
Before fix e5c6de5 tcp_read_skb() would increment the tp->copied-seq value. This (as described in the commit) would cause an error for apps because once that is incremented the application might believe there is no data to be read. Then some apps would stall or abort believing no data is available. However, the fix is incomplete because it introduces another issue in the skb dequeue. The loop does tcp_recv_skb() in a while loop to consume as many skbs as possible. The problem is the call is ... tcp_recv_skb(sk, seq, &offset) ... where 'seq' is: u32 seq = tp->copied_seq; Now we can hit a case where we've yet incremented copied_seq from BPF side, but then tcp_recv_skb() fails this test ... if (offset < skb->len || (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)) ... so that instead of returning the skb we call tcp_eat_recv_skb() which frees the skb. This is because the routine believes the SKB has been collapsed per comment: /* This looks weird, but this can happen if TCP collapsing * splitted a fat GRO packet, while we released socket lock * in skb_splice_bits() */ This can't happen here we've unlinked the full SKB and orphaned it. Anyways it would confuse any BPF programs if the data were suddenly moved underneath it. To fix this situation do simpler operation and just skb_peek() the data of the queue followed by the unlink. It shouldn't need to check this condition and tcp_read_skb() reads entire skbs so there is no need to handle the 'offset!=0' case as we would see in tcp_read_sock(). Fixes: e5c6de5 ("bpf, sockmap: Incorrectly handling copied_seq") Fixes: 04919be ("tcp: Introduce tcp_read_skb()") Signed-off-by: John Fastabend <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Reviewed-by: Jakub Sitnicki <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 81335f9 commit 9b7177b

File tree

1 file changed

+2
-8
lines changed

1 file changed

+2
-8
lines changed

net/ipv4/tcp.c

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,16 +1621,13 @@ EXPORT_SYMBOL(tcp_read_sock);
16211621

16221622
int tcp_read_skb(struct sock *sk, skb_read_actor_t recv_actor)
16231623
{
1624-
struct tcp_sock *tp = tcp_sk(sk);
1625-
u32 seq = tp->copied_seq;
16261624
struct sk_buff *skb;
16271625
int copied = 0;
1628-
u32 offset;
16291626

16301627
if (sk->sk_state == TCP_LISTEN)
16311628
return -ENOTCONN;
16321629

1633-
while ((skb = tcp_recv_skb(sk, seq, &offset)) != NULL) {
1630+
while ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) {
16341631
u8 tcp_flags;
16351632
int used;
16361633

@@ -1643,13 +1640,10 @@ int tcp_read_skb(struct sock *sk, skb_read_actor_t recv_actor)
16431640
copied = used;
16441641
break;
16451642
}
1646-
seq += used;
16471643
copied += used;
16481644

1649-
if (tcp_flags & TCPHDR_FIN) {
1650-
++seq;
1645+
if (tcp_flags & TCPHDR_FIN)
16511646
break;
1652-
}
16531647
}
16541648
return copied;
16551649
}

0 commit comments

Comments
 (0)