lkml.org 
[lkml]   [2017]   [May]   [11]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.9 084/103] tcp: do not underestimate skb->truesize in tcp_trim_head()
    Date
    4.9-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Eric Dumazet <edumazet@google.com>


    [ Upstream commit 7162fb242cb8322beb558828fd26b33c3e9fc805 ]

    Andrey found a way to trigger the WARN_ON_ONCE(delta < len) in
    skb_try_coalesce() using syzkaller and a filter attached to a TCP
    socket over loopback interface.

    I believe one issue with looped skbs is that tcp_trim_head() can end up
    producing skb with under estimated truesize.

    It hardly matters for normal conditions, since packets sent over
    loopback are never truncated.

    Bytes trimmed from skb->head should not change skb truesize, since
    skb->head is not reallocated.

    Signed-off-by: Eric Dumazet <edumazet@google.com>
    Reported-by: Andrey Konovalov <andreyknvl@google.com>
    Tested-by: Andrey Konovalov <andreyknvl@google.com>
    Signed-off-by: David S. Miller <davem@davemloft.net>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    net/ipv4/tcp_output.c | 19 ++++++++++++-------
    1 file changed, 12 insertions(+), 7 deletions(-)

    --- a/net/ipv4/tcp_output.c
    +++ b/net/ipv4/tcp_output.c
    @@ -1246,7 +1246,7 @@ int tcp_fragment(struct sock *sk, struct
    * eventually). The difference is that pulled data not copied, but
    * immediately discarded.
    */
    -static void __pskb_trim_head(struct sk_buff *skb, int len)
    +static int __pskb_trim_head(struct sk_buff *skb, int len)
    {
    struct skb_shared_info *shinfo;
    int i, k, eat;
    @@ -1256,7 +1256,7 @@ static void __pskb_trim_head(struct sk_b
    __skb_pull(skb, eat);
    len -= eat;
    if (!len)
    - return;
    + return 0;
    }
    eat = len;
    k = 0;
    @@ -1282,23 +1282,28 @@ static void __pskb_trim_head(struct sk_b
    skb_reset_tail_pointer(skb);
    skb->data_len -= len;
    skb->len = skb->data_len;
    + return len;
    }

    /* Remove acked data from a packet in the transmit queue. */
    int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len)
    {
    + u32 delta_truesize;
    +
    if (skb_unclone(skb, GFP_ATOMIC))
    return -ENOMEM;

    - __pskb_trim_head(skb, len);
    + delta_truesize = __pskb_trim_head(skb, len);

    TCP_SKB_CB(skb)->seq += len;
    skb->ip_summed = CHECKSUM_PARTIAL;

    - skb->truesize -= len;
    - sk->sk_wmem_queued -= len;
    - sk_mem_uncharge(sk, len);
    - sock_set_flag(sk, SOCK_QUEUE_SHRUNK);
    + if (delta_truesize) {
    + skb->truesize -= delta_truesize;
    + sk->sk_wmem_queued -= delta_truesize;
    + sk_mem_uncharge(sk, delta_truesize);
    + sock_set_flag(sk, SOCK_QUEUE_SHRUNK);
    + }

    /* Any change of skb->len requires recalculation of tso factor. */
    if (tcp_skb_pcount(skb) > 1)

    \
     
     \ /
      Last update: 2017-05-11 17:05    [W:4.154 / U:1.864 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site