]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
xsk: fix use-after-free of xs->skb in xsk_build_skb() free_err path
authorJason Xing <kernelxing@tencent.com>
Sat, 2 May 2026 20:07:18 +0000 (23:07 +0300)
committerJakub Kicinski <kuba@kernel.org>
Wed, 6 May 2026 02:27:50 +0000 (19:27 -0700)
When xsk_build_skb() processes multi-buffer packets in copy mode, the
first descriptor stores data into the skb linear area without adding
any frags, so nr_frags stays at 0. The caller then sets xs->skb = skb
to accumulate subsequent descriptors.

If a continuation descriptor fails (e.g. alloc_page returns NULL with
-EAGAIN), we jump to free_err where the condition:

  if (skb && !skb_shinfo(skb)->nr_frags)
      kfree_skb(skb);

evaluates to true because nr_frags is still 0 (the first descriptor
used the linear area, not frags). This frees the skb while xs->skb
still points to it, creating a dangling pointer. On the next transmit
attempt or socket close, xs->skb is dereferenced, causing a
use-after-free or double-free.

Fix by using a !xs->skb check to handle first frag situation, ensuring
we only free skbs that were freshly allocated in this call
(xs->skb is NULL) and never free an in-progress multi-buffer skb that
the caller still references.

Closes: https://lore.kernel.org/all/20260415082654.21026-4-kerneljasonxing@gmail.com/
Fixes: 6b9c129c2f93 ("xsk: remove @first_frag from xsk_build_skb()")
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Signed-off-by: Jason Xing <kernelxing@tencent.com>
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Link: https://patch.msgid.link/20260502200722.53960-5-kerneljasonxing@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/xdp/xsk.c

index 06ee260f3afc0500c33ca0d74751718f3d80a0f5..55378c3855d5d08d8f4b4b5195f820bd092d2c25 100644 (file)
@@ -972,7 +972,7 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
        return skb;
 
 free_err:
-       if (skb && !skb_shinfo(skb)->nr_frags)
+       if (skb && !xs->skb)
                kfree_skb(skb);
 
        if (err == -EOVERFLOW) {