net: fix frag_list leak in skb_segment_list() error path

skb_segment_list() detaches the frag_list from the head skb and moves
its members one by one to the skb->next chain. On error, err_linearize
frees that chain with kfree_skb_list(skb->next), relying on the members
not yet processed to still be linked after the current one.

This does not hold when the current member was shared: it is replaced
by skb_clone(), and __skb_clone() clears ->next. If __skb_linearize()
then fails on that clone, the remaining members are no longer reachable
from skb->next nor from skb_shinfo(skb)->frag_list, and they leak.

The skb_shared() error branch already relinks nskb->next to list_skb
before jumping to err_linearize. Do it unconditionally right after nskb
is added to the chain, so every error exit sees the remaining members.
On the last member list_skb is NULL, which still terminates the chain.

Fixes: 53475c5dd8 ("net: fix use-after-free when UDP GRO with shared fraglist")
Signed-off-by: Eric Dumazet <edumazet@kernel.org>
Cc: Simon Polack <spolack+git@mailbox.org>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Reviewed-by: Wang Zhan <wang.zhan@smartx.com>
Reviewed-by: Jiayuan Chen <jiayuan.chen@linux.dev>
Link: https://patch.msgid.link/20261021161858.761205-1-edumazet@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Eric Dumazet 2026-10-21 18:18:58 +02:00 committed by Jakub Kicinski
parent c3a1ddd5e1
commit 546daf3444

View File

@ -4782,10 +4782,13 @@ struct sk_buff *skb_segment_list(struct sk_buff *skb,
else
tail->next = nskb;
if (unlikely(err)) {
nskb->next = list_skb;
/* nskb may be a clone with a NULL ->next: keep the remaining
* members linked so that err_linearize frees them.
*/
nskb->next = list_skb;
if (unlikely(err))
goto err_linearize;
}
tail = nskb;