]> git.hungrycats.org Git - linux/commitdiff
tcp: detect malicious patterns in tcp_collapse_ofo_queue()
authorEric Dumazet <edumazet@google.com>
Mon, 23 Jul 2018 16:28:19 +0000 (09:28 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 28 Jul 2018 05:43:19 +0000 (07:43 +0200)
[ Upstream commit 3d4bf93ac12003f9b8e1e2de37fe27983deebdcf ]

In case an attacker feeds tiny packets completely out of order,
tcp_collapse_ofo_queue() might scan the whole rb-tree, performing
expensive copies, but not changing socket memory usage at all.

1) Do not attempt to collapse tiny skbs.
2) Add logic to exit early when too many tiny skbs are detected.

We prefer not doing aggressive collapsing (which copies packets)
for pathological flows, and revert to tcp_prune_ofo_queue() which
will be less expensive.

In the future, we might add the possibility of terminating flows
that are proven to be malicious.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Soheil Hassas Yeganeh <soheil@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
net/ipv4/tcp_input.c

index 7dd31aec8b482e04f5e4a30f756c449a9255dc56..dee70277f12fc42e0cc4b002e5800c7b8806f479 100644 (file)
@@ -4652,6 +4652,7 @@ restart:
 static void tcp_collapse_ofo_queue(struct sock *sk)
 {
        struct tcp_sock *tp = tcp_sk(sk);
+       u32 range_truesize, sum_tiny = 0;
        struct sk_buff *skb = skb_peek(&tp->out_of_order_queue);
        struct sk_buff *head;
        u32 start, end;
@@ -4661,6 +4662,7 @@ static void tcp_collapse_ofo_queue(struct sock *sk)
 
        start = TCP_SKB_CB(skb)->seq;
        end = TCP_SKB_CB(skb)->end_seq;
+       range_truesize = skb->truesize;
        head = skb;
 
        for (;;) {
@@ -4675,14 +4677,24 @@ static void tcp_collapse_ofo_queue(struct sock *sk)
                if (!skb ||
                    after(TCP_SKB_CB(skb)->seq, end) ||
                    before(TCP_SKB_CB(skb)->end_seq, start)) {
-                       tcp_collapse(sk, &tp->out_of_order_queue,
-                                    head, skb, start, end);
+                       /* Do not attempt collapsing tiny skbs */
+                       if (range_truesize != head->truesize ||
+                           end - start >= SKB_WITH_OVERHEAD(SK_MEM_QUANTUM)) {
+                               tcp_collapse(sk, &tp->out_of_order_queue,
+                                            head, skb, start, end);
+                       } else {
+                               sum_tiny += range_truesize;
+                               if (sum_tiny > sk->sk_rcvbuf >> 3)
+                                       return;
+                       }
+
                        head = skb;
                        if (!skb)
                                break;
                        /* Start new segment */
                        start = TCP_SKB_CB(skb)->seq;
                        end = TCP_SKB_CB(skb)->end_seq;
+                       range_truesize = skb->truesize;
                } else {
                        if (before(TCP_SKB_CB(skb)->seq, start))
                                start = TCP_SKB_CB(skb)->seq;