From: lena wang lena.wang@mediatek.com
stable inclusion from stable-v5.10.104 commit 4e178ed14bda47942c1ccad3f60b774870b45db9 bugzilla: https://gitee.com/openeuler/kernel/issues/I56XAC
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
--------------------------------
commit 224102de2ff105a2c05695e66a08f4b5b6b2d19c upstream.
The truesize for a UDP GRO packet is added by main skb and skbs in main skb's frag_list: skb_gro_receive_list p->truesize += skb->truesize;
The commit 53475c5dd856 ("net: fix use-after-free when UDP GRO with shared fraglist") introduced a truesize increase for frag_list skbs. When uncloning skb, it will call pskb_expand_head and trusesize for frag_list skbs may increase. This can occur when allocators uses __netdev_alloc_skb and not jump into __alloc_skb. This flow does not use ksize(len) to calculate truesize while pskb_expand_head uses. skb_segment_list err = skb_unclone(nskb, GFP_ATOMIC); pskb_expand_head if (!skb->sk || skb->destructor == sock_edemux) skb->truesize += size - osize;
If we uses increased truesize adding as delta_truesize, it will be larger than before and even larger than previous total truesize value if skbs in frag_list are abundant. The main skb truesize will become smaller and even a minus value or a huge value for an unsigned int parameter. Then the following memory check will drop this abnormal skb.
To avoid this error we should use the original truesize to segment the main skb.
Fixes: 53475c5dd856 ("net: fix use-after-free when UDP GRO with shared fraglist") Signed-off-by: lena wang lena.wang@mediatek.com Acked-by: Paolo Abeni pabeni@redhat.com Reviewed-by: Eric Dumazet edumazet@google.com Link: https://lore.kernel.org/r/1646133431-8948-1-git-send-email-lena.wang@mediate... Signed-off-by: Jakub Kicinski kuba@kernel.org Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Yu Liao liaoyu15@huawei.com Reviewed-by: Wei Li liwei391@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- net/core/skbuff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c index d1ecbf6f1535..0e92b74ce3b1 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3695,6 +3695,7 @@ struct sk_buff *skb_segment_list(struct sk_buff *skb, list_skb = list_skb->next;
err = 0; + delta_truesize += nskb->truesize; if (skb_shared(nskb)) { tmp = skb_clone(nskb, GFP_ATOMIC); if (tmp) { @@ -3719,7 +3720,6 @@ struct sk_buff *skb_segment_list(struct sk_buff *skb, tail = nskb;
delta_len += nskb->len; - delta_truesize += nskb->truesize;
skb_push(nskb, -skb_network_offset(nskb) + offset);