Commit 3dec153a authored by Jason Xing's avatar Jason Xing Committed by Jakub Kicinski
Browse files

xsk: prevent CQ desync when freeing half-built skbs in xsk_build_skb()

Once xsk_skb_init_misc() has been called on an skb, its destructor is
set to xsk_destruct_skb(), which submits the descriptor address(es) to
the completion queue and advances the CQ producer. If such an skb is
subsequently freed via kfree_skb() along an error path - before the
skb has ever been handed to the driver - the destructor still runs and
submits a bogus, half-initialized address to the CQ.

Postpone the init phase when we believe the allocation of first frag is
successfully completed. Before this init, skb can be safely freed by
kfree_skb().

Closes: https://lore.kernel.org/all/20260419045822.843BFC2BCAF@smtp.kernel.org/


Fixes: c30d0849 ("xsk: avoid overwriting skb fields for multi-buffer traffic")
Acked-by: default avatarStanislav Fomichev <sdf@fomichev.me>
Signed-off-by: default avatarJason Xing <kernelxing@tencent.com>
Reviewed-by: default avatarAlexander Lobakin <aleksander.lobakin@intel.com>
Link: https://patch.msgid.link/20260502200722.53960-6-kerneljasonxing@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 0f377658
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -819,8 +819,6 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
			return ERR_PTR(err);

		skb_reserve(skb, hr);

		xsk_skb_init_misc(skb, xs, desc->addr);
		if (desc->options & XDP_TX_METADATA) {
			err = xsk_skb_metadata(skb, buffer, desc, pool, hr);
			if (unlikely(err))
@@ -917,7 +915,6 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
			if (unlikely(err))
				goto free_err;

			xsk_skb_init_misc(skb, xs, desc->addr);
			if (desc->options & XDP_TX_METADATA) {
				err = xsk_skb_metadata(skb, buffer, desc,
						       xs->pool, hr);
@@ -967,6 +964,8 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
		}
	}

	if (!xs->skb)
		xsk_skb_init_misc(skb, xs, desc->addr);
	xsk_inc_num_desc(skb);

	return skb;