xfrm: fix stale skb->prev after async crypto steals a GSO segment

skb_gso_segment() leaves the segment list head with ->prev pointing at
the last segment, an invariant validate_xmit_skb_list() relies on when
it sets its tail pointer (tail = skb->prev).

When validate_xmit_xfrm() walks a GSO list and some segments are stolen
by async crypto (->xmit() returns -EINPROGRESS), those segments are
unlinked from the list but the head ->prev is never updated.  If the
last segment is the one stolen, the returned head still has ->prev
pointing at it, even though it is now owned by the crypto engine and may
be freed.  validate_xmit_skb_list() later does tail->next = skb, writing
through that stale pointer -- a use-after-free.

Repoint skb->prev at the last retained segment before returning.

Fixes: f53c723902 ("net: Add asynchronous callbacks for xfrm on layer 2.")
Signed-off-by: Petr Wozniak <petr.wozniak@gmail.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
This commit is contained in:
Petr Wozniak 2026-06-21 12:03:27 +02:00 committed by Steffen Klassert
parent 6860b467f5
commit 3f4c3919ba

View File

@ -224,6 +224,14 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
pskb = skb2;
}
/* skb_gso_segment() set skb->prev to the last segment, but async
* crypto may have stolen it above without updating ->prev. Repoint
* it at the last retained segment so validate_xmit_skb_list() does
* not chain onto a segment now owned by the crypto engine.
*/
if (skb)
skb->prev = pskb;
return skb ? skb : ERR_PTR(-EINPROGRESS);
}
EXPORT_SYMBOL_GPL(validate_xmit_xfrm);