net: gro: fix possible panic in skb_gro_receive()
authorEric Dumazet <edumazet@google.com>
Thu, 6 Dec 2012 13:54:59 +0000 (13:54 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 7 Dec 2012 19:39:29 +0000 (14:39 -0500)
commit 2e71a6f8084e (net: gro: selective flush of packets) added
a bug for skbs using frag_list. This part of the GRO stack is rarely
used, as it needs skb not using a page fragment for their skb->head.

Most drivers do use a page fragment, but some of them use GFP_KERNEL
allocations for the initial fill of their RX ring buffer.

napi_gro_flush() overwrite skb->prev that was used for these skb to
point to the last skb in frag_list.

Fix this using a separate field in struct napi_gro_cb to point to the
last fragment.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netdevice.h
net/core/dev.c
net/core/skbuff.c

index f8eda0276f03fadf3be56e8e70f928f792714406..a848ffc327f4f19e3b1da926f52aa361fd27b654 100644 (file)
@@ -1488,6 +1488,9 @@ struct napi_gro_cb {
 
        /* Used in ipv6_gro_receive() */
        int     proto;
+
+       /* used in skb_gro_receive() slow path */
+       struct sk_buff *last;
 };
 
 #define NAPI_GRO_CB(skb) ((struct napi_gro_cb *)(skb)->cb)
index c0946cb2b3547f4fdea0fa56b8f7e17c71a75012..e5942bf45a6d9b7e7412a2100b7ef116610aab65 100644 (file)
@@ -3451,6 +3451,8 @@ static int napi_gro_complete(struct sk_buff *skb)
        struct list_head *head = &ptype_base[ntohs(type) & PTYPE_HASH_MASK];
        int err = -ENOENT;
 
+       BUILD_BUG_ON(sizeof(struct napi_gro_cb) > sizeof(skb->cb));
+
        if (NAPI_GRO_CB(skb)->count == 1) {
                skb_shinfo(skb)->gso_size = 0;
                goto out;
index 4007c1437fdaf615bbf197ad52e545529e7bce59..3f0636cd76cdcd132278e4f860ee1f581fd8174c 100644 (file)
@@ -3004,7 +3004,7 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
        skb_shinfo(nskb)->gso_size = pinfo->gso_size;
        pinfo->gso_size = 0;
        skb_header_release(p);
-       nskb->prev = p;
+       NAPI_GRO_CB(nskb)->last = p;
 
        nskb->data_len += p->len;
        nskb->truesize += p->truesize;
@@ -3030,8 +3030,8 @@ merge:
 
        __skb_pull(skb, offset);
 
-       p->prev->next = skb;
-       p->prev = skb;
+       NAPI_GRO_CB(p)->last->next = skb;
+       NAPI_GRO_CB(p)->last = skb;
        skb_header_release(skb);
 
 done: