| From 097b9146c0e26aabaa6ff3e5ea536a53f5254a79 Mon Sep 17 00:00:00 2001 |
| From: Marco Elver <elver@google.com> |
| Date: Mon, 1 Feb 2021 17:04:20 +0100 |
| Subject: net: fix up truesize of cloned skb in skb_prepare_for_shift() |
| |
| From: Marco Elver <elver@google.com> |
| |
| commit 097b9146c0e26aabaa6ff3e5ea536a53f5254a79 upstream. |
| |
| Avoid the assumption that ksize(kmalloc(S)) == ksize(kmalloc(S)): when |
| cloning an skb, save and restore truesize after pskb_expand_head(). This |
| can occur if the allocator decides to service an allocation of the same |
| size differently (e.g. use a different size class, or pass the |
| allocation on to KFENCE). |
| |
| Because truesize is used for bookkeeping (such as sk_wmem_queued), a |
| modified truesize of a cloned skb may result in corrupt bookkeeping and |
| relevant warnings (such as in sk_stream_kill_queues()). |
| |
| Link: https://lkml.kernel.org/r/X9JR/J6dMMOy1obu@elver.google.com |
| Reported-by: syzbot+7b99aafdcc2eedea6178@syzkaller.appspotmail.com |
| Suggested-by: Eric Dumazet <edumazet@google.com> |
| Signed-off-by: Marco Elver <elver@google.com> |
| Signed-off-by: Eric Dumazet <edumazet@google.com> |
| Link: https://lore.kernel.org/r/20210201160420.2826895-1-elver@google.com |
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/core/skbuff.c | 14 +++++++++++++- |
| 1 file changed, 13 insertions(+), 1 deletion(-) |
| |
| --- a/net/core/skbuff.c |
| +++ b/net/core/skbuff.c |
| @@ -3292,7 +3292,19 @@ EXPORT_SYMBOL(skb_split); |
| */ |
| static int skb_prepare_for_shift(struct sk_buff *skb) |
| { |
| - return skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC); |
| + int ret = 0; |
| + |
| + if (skb_cloned(skb)) { |
| + /* Save and restore truesize: pskb_expand_head() may reallocate |
| + * memory where ksize(kmalloc(S)) != ksize(kmalloc(S)), but we |
| + * cannot change truesize at this point. |
| + */ |
| + unsigned int save_truesize = skb->truesize; |
| + |
| + ret = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); |
| + skb->truesize = save_truesize; |
| + } |
| + return ret; |
| } |
| |
| /** |