| From: Eric Dumazet <edumazet@google.com> |
| Date: Thu, 14 Mar 2013 05:40:32 +0000 |
| Subject: tcp: fix skb_availroom() |
| |
| [ Upstream commit 16fad69cfe4adbbfa813de516757b87bcae36d93 ] |
| |
| Chrome OS team reported a crash on a Pixel ChromeBook in TCP stack : |
| |
| https://code.google.com/p/chromium/issues/detail?id=182056 |
| |
| commit a21d45726acac (tcp: avoid order-1 allocations on wifi and tx |
| path) did a poor choice adding an 'avail_size' field to skb, while |
| what we really needed was a 'reserved_tailroom' one. |
| |
| It would have avoided commit 22b4a4f22da (tcp: fix retransmit of |
| partially acked frames) and this commit. |
| |
| Crash occurs because skb_split() is not aware of the 'avail_size' |
| management (and should not be aware) |
| |
| Signed-off-by: Eric Dumazet <edumazet@google.com> |
| Reported-by: Mukesh Agrawal <quiche@chromium.org> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| include/linux/skbuff.h | 7 +++++-- |
| net/ipv4/tcp.c | 2 +- |
| net/ipv4/tcp_output.c | 1 - |
| 3 files changed, 6 insertions(+), 4 deletions(-) |
| |
| diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h |
| index 53dc7e7..da65890 100644 |
| --- a/include/linux/skbuff.h |
| +++ b/include/linux/skbuff.h |
| @@ -455,7 +455,7 @@ struct sk_buff { |
| union { |
| __u32 mark; |
| __u32 dropcount; |
| - __u32 avail_size; |
| + __u32 reserved_tailroom; |
| }; |
| |
| __u16 vlan_tci; |
| @@ -1332,7 +1332,10 @@ static inline int skb_tailroom(const struct sk_buff *skb) |
| */ |
| static inline int skb_availroom(const struct sk_buff *skb) |
| { |
| - return skb_is_nonlinear(skb) ? 0 : skb->avail_size - skb->len; |
| + if (skb_is_nonlinear(skb)) |
| + return 0; |
| + |
| + return skb->end - skb->tail - skb->reserved_tailroom; |
| } |
| |
| /** |
| diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c |
| index 52edbb8..fe381c2 100644 |
| --- a/net/ipv4/tcp.c |
| +++ b/net/ipv4/tcp.c |
| @@ -704,7 +704,7 @@ struct sk_buff *sk_stream_alloc_skb(struct sock *sk, int size, gfp_t gfp) |
| * Make sure that we have exactly size bytes |
| * available to the caller, no more, no less. |
| */ |
| - skb->avail_size = size; |
| + skb->reserved_tailroom = skb->end - skb->tail - size; |
| return skb; |
| } |
| __kfree_skb(skb); |
| diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c |
| index 921cbac..9bb7400 100644 |
| --- a/net/ipv4/tcp_output.c |
| +++ b/net/ipv4/tcp_output.c |
| @@ -1096,7 +1096,6 @@ static void __pskb_trim_head(struct sk_buff *skb, int len) |
| eat = min_t(int, len, skb_headlen(skb)); |
| if (eat) { |
| __skb_pull(skb, eat); |
| - skb->avail_size -= eat; |
| len -= eat; |
| if (!len) |
| return; |