Skip to contentSkip to conversation

xsk: fix bugs around xsk skb allocation

PATCH Superseded Jason Xing 6 participants 25 messages Apr 22 – Apr 27

Subsystems (4)

v3 of 5 Superseded Latest is v5 →

lore
Share & tools
lore

Jason Xing

author
#0 lore raw

v3 cover xsk: fix bugs around xsk skb allocation

From: Jason Xing <[email protected]>

There are rare issues around xsk_build_skb(). Some of them were founded by Sashiko[1][2].

[1]: https://lore.kernel.org/all/[email protected]/
[2]: https://lore.kernel.org/all/[email protected]/

---
v3

1. use !xs->skb as the indicator of first frag in patch 3 (Stan)
2. move init skb after it's allocated successfully, so patch 4,5,6 have been adjusted accordingly (Stan)
3. do not support 32-bit arch (Stan)
4. add acked-by tags (Stan)

v2

1. add four patches spotted by sashiko to fix buggy pre-existing behavior
2. adjust the order of 8 patches.

Jason Xing (8):
xsk: reject sw-csum UMEM binding to IFF_TX_SKB_NO_LINEAR devices
xsk: handle NULL dereference of the skb without frags issue
xsk: fix use-after-free of xs->skb in xsk_build_skb() free_err path
xsk: prevent CQ desync when freeing half-built skbs in xsk_build_skb()
xsk: avoid skb leak in XDP_TX_METADATA case
xsk: free the skb when hitting the upper bound MAX_SKB_FRAGS
xsk: fix xsk_addrs slab leak on multi-buffer error path
xsk: don't support AF_XDP on 32-bit architectures

 net/xdp/Kconfig         |  2 +-
 net/xdp/xsk.c           | 31 ++++++++++++++++++++-----------
 net/xdp/xsk_buff_pool.c |  3 +++
 3 files changed, 24 insertions(+), 12 deletions(-)
Signature · 1 line
2.41.3
author
#1 lore raw

PATCH v3 1/8 xsk: reject sw-csum UMEM binding to IFF_TX_SKB_NO_LINEAR devices

From: Jason Xing <[email protected]>

skb_checksum_help() is a common helper that writes the folded 16-bit checksum back via skb->data + csum_start + csum_offset, i.e. it relies on the skb's linear head and fails (with WARN_ONCE and -EINVAL) when skb_headlen() is 0.

AF_XDP generic xmit takes two very different paths depending on the netdev. Drivers that advertise IFF_TX_SKB_NO_LINEAR (e.g. virtio_net) skip the "copy payload into a linear head" step on purpose as a performance optimisation: xsk_build_skb_zerocopy() only attaches UMEM pages as frags and never calls skb_put(), so skb_headlen() stays 0 for the whole skb. For these skbs there is simply no linear area for skb_checksum_help() to write the csum into - the sw-csum fallback is structurally inapplicable.

The patch tries to catch this and reject the combination with error at setup time. Rejecting at bind() converts this silent per-packet failure into a synchronous, actionable -EOPNOTSUPP at setup time. HW csum and launch_time metadata on IFF_TX_SKB_NO_LINEAR drivers are unaffected because they do not call skb_checksum_help().

Without the patch, every descriptor carrying 'XDP_TX_METADATA | XDP_TXMD_FLAGS_CHECKSUM' produces:
1) a WARN_ONCE "offset (N) >= skb_headlen() (0)" from skb_checksum_help(),
2) sendmsg() returning -EINVAL without consuming the descriptor (invalid_descs is not incremented),
3) a wedged TX ring: __xsk_generic_xmit() does not advance the consumer on non-EOVERFLOW errors, so the next sendmsg() re-reads the same descriptor and re-hits the same WARN until the socket is closed.

Fixes 30c3055f9c0d ("xsk: wrap generic metadata handling onto separate function")
---
 net/xdp/xsk_buff_pool.c | 3 +++
 1 file changed, 3 insertions(+)
net/xdp/xsk_buff_pool.c +3 -0
index 37b7a68b89b3..c2521b6547e3 100644
@@ -169,6 +169,9 @@ int xp_assign_dev(struct xsk_buff_pool *pool,
 	if (force_zc && force_copy)
 		return -EINVAL;
 
+	if (pool->tx_sw_csum && (netdev->priv_flags & IFF_TX_SKB_NO_LINEAR))
+		return -EOPNOTSUPP;
+
 	if (xsk_get_pool_from_qid(netdev, queue_id))
 		return -EBUSY;
 
Signature · 1 line
2.41.3
author
#2 lore raw

PATCH v3 2/8 xsk: handle NULL dereference of the skb without frags issue

In reply to Jason Xing

From: Jason Xing <[email protected]>

When a first descriptor (xs->skb == NULL) triggers -EOVERFLOW in xsk_build_skb_zerocopy (e.g., MAX_SKB_FRAGS exceeded), the free_err EOVERFLOW handler unconditionally dereferences xs->skb via xsk_inc_num_desc(xs->skb) and xsk_drop_skb(xs->skb), causing a NULL pointer dereference.

In this series, the skb is already freed by kfree_skb() inside xsk_build_skb_zerocopy for the first-descriptor case, so we only need to do the bookkeeping: cancel the one reserved CQ slot and account for the single invalid descriptor.

Guard the existing xsk_inc_num_desc/xsk_drop_skb calls with an xs->skb check (for the continuation case), and add an else branch for the first-descriptor case that manually cancels the CQ slot and increments invalid_descs by one.

Fixes cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path")
---
 net/xdp/xsk.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)
net/xdp/xsk.c +8 -3
index 6149f6a79897..6521604f8d42 100644
@@ -893,9 +893,14 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
 		kfree_skb(skb);
 
 	if (err == -EOVERFLOW) {
-		/* Drop the packet */
-		xsk_inc_num_desc(xs->skb);
-		xsk_drop_skb(xs->skb);
+		if (xs->skb) {
+			/* Drop the packet */
+			xsk_inc_num_desc(xs->skb);
+			xsk_drop_skb(xs->skb);
+		} else {
+			xsk_cq_cancel_locked(xs->pool, 1);
+			xs->tx->invalid_descs++;
+		}
 		xskq_cons_release(xs->tx);
 	} else {
 		/* Let application retry */
Signature · 1 line
2.41.3
author
#3 lore raw

PATCH v3 3/8 xsk: fix use-after-free of xs->skb in xsk_build_skb() free_err path

In reply to Jason Xing

From: Jason Xing <[email protected]>

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.

Fixes 6b9c129c2f93 ("xsk: remove @first_frag from xsk_build_skb()")
---
 net/xdp/xsk.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/xdp/xsk.c +1 -1
index 6521604f8d42..d23d1b14b8b4 100644
@@ -889,7 +889,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) {
Signature · 1 line
2.41.3
Acked-by on v3 3/8
#4 lore raw

Re: [PATCH net v3 3/8] xsk: fix use-after-free of xs->skb in xsk_build_skb() free_err path

Quoted 45 lines

From: Jason Xing <[email protected]>

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.

Fixes 6b9c129c2f93 ("xsk: remove @first_frag from xsk_build_skb()")
---
 net/xdp/xsk.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/xdp/xsk.c +1 -2
index 6521604f8d42..d23d1b14b8b4 100644
@@ -889,7 +889,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) {
--
Quoted 2 lines

2.41.3

#5 lore raw

Re: [PATCH net v3 3/8] xsk: fix use-after-free of xs->skb in xsk_build_skb() free_err path

In reply to Jason Xing
Sashiko AI review found 2 potential issue(s): - [High] Incorrect completion address reporting to user-space and leaked descriptor on `-EOVERFLOW` in multi-buffer packets. - [High] Packet stream…

Sashiko AI review found 2 potential issue(s):
- [High] Incorrect completion address reporting to user-space and leaked descriptor on -EOVERFLOW in multi-buffer packets.
- [High] Packet stream corruption when dropping oversized multi-buffer packets.
--

commit 38322c7d2a85e9ef029404cbf4ae750b54520d27
Author: Jason Xing <[email protected]>
xsk: fix use-after-free of xs->skb in xsk_build_skb() free_err path

This patch fixes a use-after-free in the xsk_build_skb() error path by ensuring that in-progress multi-buffer socket buffers are not freed if they are still referenced by the xdp_sock.

net/xdp/xsk.c static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, · line 975

Quoted 12 lines
net/xdp/xsk.c +1 -1
@@ -969,7 +969,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) {

I noticed a couple of pre-existing regressions in this error path.

Do the error handling paths for -EOVERFLOW leak the original descriptor address and submit an invalid address to user-space?

When a continuation descriptor exceeds the fragment limit, the code jumps to the free_err label before the current descriptor address is stored in the xsk_addr array.

However, the error path still increments the descriptor count:

        if (xs->skb) {
            /* Drop the packet */
            xsk_inc_num_desc(xs->skb);
            xsk_drop_skb(xs->skb);
        }

When xsk_destruct_skb() loops up to the new descriptor count, could it read past the initialized entries, find a zero address from the earlier kmem_cache_zalloc(), and submit address 0 to the completion queue instead of the actual dropped address?

Does dropping the socket buffer here cause packet stream corruption?

When a multi-buffer packet is dropped due to an -EOVERFLOW error, the partial packet is discarded and the socket buffer pointer is cleared.

Because there is no state tracking to discard the remaining descriptors of the dropped packet, will the transmission loop process the next descriptor (which is just a continuation of the dropped packet) as the beginning of a completely new packet?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
author
#6 lore raw

Re: [PATCH net v3 3/8] xsk: fix use-after-free of xs->skb in xsk_build_skb() free_err path

Quoted 22 lines · On Sat, Apr 25, 2026 at 12:17 PM <[email protected]> wrote:

Sashiko AI review found 2 potential issue(s):
- [High] Incorrect completion address reporting to user-space and leaked descriptor on -EOVERFLOW in multi-buffer packets.
- [High] Packet stream corruption when dropping oversized multi-buffer packets.
--

commit 38322c7d2a85e9ef029404cbf4ae750b54520d27
Author: Jason Xing <[email protected]>
xsk: fix use-after-free of xs->skb in xsk_build_skb() free_err path

This patch fixes a use-after-free in the xsk_build_skb() error path by ensuring that in-progress multi-buffer socket buffers are not freed if they are still referenced by the xdp_sock.

Quoted 6 lines
net/xdp/xsk.c +0 -0
@@ -969,7 +969,7 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
      return skb;
 
Quoted 8 lines

net/xdp/xsk.c static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, · line 895

Quoted 6 lines
net/xdp/xsk.c +1 -1
 free_err:
-     if (skb && !skb_shinfo(skb)->nr_frags)
+     if (skb && !xs->skb)
              kfree_skb(skb);
 
      if (err == -EOVERFLOW) {

I noticed a couple of pre-existing regressions in this error path.

Good job. I'll cook a separate patchset to fix this new bug as a follow-up instead of polluting the current series :)

Quoted 16 lines

Do the error handling paths for -EOVERFLOW leak the original descriptor address and submit an invalid address to user-space?

When a continuation descriptor exceeds the fragment limit, the code jumps to the free_err label before the current descriptor address is stored in the xsk_addr array.

However, the error path still increments the descriptor count:

        if (xs->skb) {
            /* Drop the packet */
            xsk_inc_num_desc(xs->skb);
            xsk_drop_skb(xs->skb);
        }
Quoted 4 lines

When xsk_destruct_skb() loops up to the new descriptor count, could it read past the initialized entries, find a zero address from the earlier kmem_cache_zalloc(), and submit address 0 to the completion queue instead of the actual dropped address?

Exactly! It doesn't have any change to run:

        xsk_addr->addrs[xsk_addr->num_descs] = desc->addr;

At the stage of destruction, the right address has never been put into the completion queue, so the application will not be able to reclaim them.

Quoted 10 lines

Does dropping the socket buffer here cause packet stream corruption?

When a multi-buffer packet is dropped due to an -EOVERFLOW error, the partial packet is discarded and the socket buffer pointer is cleared.

Because there is no state tracking to discard the remaining descriptors of the dropped packet, will the transmission loop process the next descriptor (which is just a continuation of the dropped packet) as the beginning of a completely new packet?

It's another critical issue but unlikely to occur. It indeed happens when the num of frags exceeds 17 as default. The remaining body of the packet will be treated as the start frag of a new packet since the old skb carrying 17 frags is freed.

Thanks,
Jason

Quoted 3 lines

--
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3

author
#7 lore raw

PATCH v3 4/8 xsk: prevent CQ desync when freeing half-built skbs in xsk_build_skb()

In reply to Jason Xing

From: Jason Xing <[email protected]>

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().

Fixes c30d084960cf ("xsk: avoid overwriting skb fields for multi-buffer traffic")
---
 net/xdp/xsk.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
net/xdp/xsk.c +2 -3
index d23d1b14b8b4..88ec6d2cbbcf 100644
@@ -739,8 +739,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))
@@ -834,7 +832,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);
@@ -884,6 +881,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;
Signature · 1 line
2.41.3
Acked-by on v3 4/8
#8 lore raw

Re: [PATCH net v3 4/8] xsk: prevent CQ desync when freeing half-built skbs in xsk_build_skb()

Quoted 51 lines

From: Jason Xing <[email protected]>

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().

Fixes c30d084960cf ("xsk: avoid overwriting skb fields for multi-buffer traffic")
---
 net/xdp/xsk.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
net/xdp/xsk.c +2 -4
index d23d1b14b8b4..88ec6d2cbbcf 100644
@@ -739,8 +739,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))
@@ -834,7 +832,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);
@@ -884,6 +881,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;
--
Quoted 2 lines

2.41.3

author
#9 lore raw

PATCH v3 5/8 xsk: avoid skb leak in XDP_TX_METADATA case

In reply to Jason Xing

From: Jason Xing <[email protected]>

Fix it by explicitly adding kfree_skb() before returning back to its caller.

How to reproduce it in virtio_net:
1. the current skb is the first one (which means no frag and xs->skb is NULL) and users enable metadata feature.
2. xsk_skb_metadata() returns a error code.
3. the caller xsk_build_skb() clears skb by using 'skb = NULL;'.
4. there is no chance to free this skb anymore.

Fixes 30c3055f9c0d ("xsk: wrap generic metadata handling onto separate function")
---
 net/xdp/xsk.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
net/xdp/xsk.c +3 -1
index 88ec6d2cbbcf..c49b58199d2f 100644
@@ -741,8 +741,10 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
 		skb_reserve(skb, hr);
 		if (desc->options & XDP_TX_METADATA) {
 			err = xsk_skb_metadata(skb, buffer, desc, pool, hr);
-			if (unlikely(err))
+			if (unlikely(err)) {
+				kfree_skb(skb);
 				return ERR_PTR(err);
+			}
 		}
 	} else {
 		struct xsk_addrs *xsk_addr;
Signature · 1 line
2.41.3
Acked-by on v3 5/8
#10 lore raw

Re: [PATCH net v3 5/8] xsk: avoid skb leak in XDP_TX_METADATA case

Quoted 36 lines

From: Jason Xing <[email protected]>

Fix it by explicitly adding kfree_skb() before returning back to its caller.

How to reproduce it in virtio_net:
1. the current skb is the first one (which means no frag and xs->skb is NULL) and users enable metadata feature.
2. xsk_skb_metadata() returns a error code.
3. the caller xsk_build_skb() clears skb by using 'skb = NULL;'.
4. there is no chance to free this skb anymore.

Fixes 30c3055f9c0d ("xsk: wrap generic metadata handling onto separate function")
---
 net/xdp/xsk.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
net/xdp/xsk.c +3 -2
index 88ec6d2cbbcf..c49b58199d2f 100644
@@ -741,8 +741,10 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
 		skb_reserve(skb, hr);
 		if (desc->options & XDP_TX_METADATA) {
 			err = xsk_skb_metadata(skb, buffer, desc, pool, hr);
-			if (unlikely(err))
+			if (unlikely(err)) {
+				kfree_skb(skb);
 				return ERR_PTR(err);
+			}
 		}
 	} else {
 		struct xsk_addrs *xsk_addr;
--
Quoted 2 lines

2.41.3

author
#11 lore raw

PATCH v3 6/8 xsk: free the skb when hitting the upper bound MAX_SKB_FRAGS

In reply to Jason Xing

From: Jason Xing <[email protected]>

Fix it by explicitly adding kfree_skb() before returning back to its caller.

How to reproduce it in virtio_net:
1. the current skb is the first one (which means xs->skb is NULL) and hit the limit MAX_SKB_FRAGS.
2. xsk_build_skb_zerocopy() returns -EOVERFLOW.
3. the caller xsk_build_skb() clears skb by using 'skb = NULL;'. This is why bug can be triggered.
4. there is no chance to free this skb anymore.

Note that if in this case the xs->skb is not NULL, xsk_build_skb() will call xsk_drop_skb(xs->skb) to do the right thing.

Fixes cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path")
---
 net/xdp/xsk.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
net/xdp/xsk.c +4 -1
index c49b58199d2f..5e6326e076ab 100644
@@ -776,8 +776,11 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
 	addr = buffer - pool->addrs;
 
 	for (copied = 0, i = skb_shinfo(skb)->nr_frags; copied < len; i++) {
-		if (unlikely(i >= MAX_SKB_FRAGS))
+		if (unlikely(i >= MAX_SKB_FRAGS)) {
+			if (!xs->skb)
+				kfree_skb(skb);
 			return ERR_PTR(-EOVERFLOW);
+		}
 
 		page = pool->umem->pgs[addr >> PAGE_SHIFT];
 		get_page(page);
Signature · 1 line
2.41.3
Acked-by on v3 6/8
#12 lore raw

Re: [PATCH net v3 6/8] xsk: free the skb when hitting the upper bound MAX_SKB_FRAGS

Quoted 40 lines

From: Jason Xing <[email protected]>

Fix it by explicitly adding kfree_skb() before returning back to its caller.

How to reproduce it in virtio_net:
1. the current skb is the first one (which means xs->skb is NULL) and hit the limit MAX_SKB_FRAGS.
2. xsk_build_skb_zerocopy() returns -EOVERFLOW.
3. the caller xsk_build_skb() clears skb by using 'skb = NULL;'. This is why bug can be triggered.
4. there is no chance to free this skb anymore.

Note that if in this case the xs->skb is not NULL, xsk_build_skb() will call xsk_drop_skb(xs->skb) to do the right thing.

Fixes cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path")
---
 net/xdp/xsk.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
net/xdp/xsk.c +4 -2
index c49b58199d2f..5e6326e076ab 100644
@@ -776,8 +776,11 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
 	addr = buffer - pool->addrs;
 
 	for (copied = 0, i = skb_shinfo(skb)->nr_frags; copied < len; i++) {
-		if (unlikely(i >= MAX_SKB_FRAGS))
+		if (unlikely(i >= MAX_SKB_FRAGS)) {
+			if (!xs->skb)
+				kfree_skb(skb);
 			return ERR_PTR(-EOVERFLOW);
+		}
 
 		page = pool->umem->pgs[addr >> PAGE_SHIFT];
 		get_page(page);
--
Quoted 2 lines

2.41.3

author
#13 lore raw

PATCH v3 7/8 xsk: fix xsk_addrs slab leak on multi-buffer error path

In reply to Jason Xing

From: Jason Xing <[email protected]>

When xsk_build_skb() / xsk_build_skb_zerocopy() sees the first continuation descriptor, it promotes destructor_arg from an inlined address to a freshly allocated xsk_addrs (num_descs = 1). The counter is bumped to >= 2 only at the very end of a successful build (by calling xsk_inc_num_desc()).

If the build fails in between (e.g. alloc_page() returns NULL with -EAGAIN, or the MAX_SKB_FRAGS overflow hits), we jump to free_err, skip calling xsk_inc_num_desc() to increment num_descs and leave the half-built skb attached to xs->skb for the app to retry. The skb now has
1) destructor_arg = a real xsk_addrs pointer,
2) num_descs = 1

If the app never retries and just close()s the socket, xsk_release() calls xsk_drop_skb() -> xsk_consume_skb(), which decides whether to free xsk_addrs by testing num_descs > 1:

if (unlikely(num_descs > 1))
kmem_cache_free(xsk_tx_generic_cache, destructor_arg);

Because num_descs is exactly 1 the branch is skipped and the xsk_addrs object is leaked to the xsk_tx_generic_cache slab.

Fix it by directly testing if destructor_arg is still addr. Or else it is modified and used to store the newly allocated memory from xsk_tx_generic_cache regardless of increment of num_desc, which we need to handle.

Fixes 0ebc27a4c67d ("xsk: avoid data corruption on cq descriptor number")
---
 net/xdp/xsk.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/xdp/xsk.c +2 -2
index 5e6326e076ab..ed96f6ec8ff2 100644
@@ -605,7 +605,7 @@ static void xsk_cq_submit_addr_locked(struct xsk_buff_pool *pool,
 	spin_lock_irqsave(&pool->cq_prod_lock, flags);
 	idx = xskq_get_prod(pool->cq);
 
-	if (unlikely(num_descs > 1)) {
+	if (unlikely(!xsk_skb_destructor_is_addr(skb))) {
 		xsk_addr = (struct xsk_addrs *)skb_shinfo(skb)->destructor_arg;
 
 		for (i = 0; i < num_descs; i++) {
@@ -660,7 +660,7 @@ static void xsk_consume_skb(struct sk_buff *skb)
 	u32 num_descs = xsk_get_num_desc(skb);
 	struct xsk_addrs *xsk_addr;
 
-	if (unlikely(num_descs > 1)) {
+	if (unlikely(!xsk_skb_destructor_is_addr(skb))) {
 		xsk_addr = (struct xsk_addrs *)skb_shinfo(skb)->destructor_arg;
 		kmem_cache_free(xsk_tx_generic_cache, xsk_addr);
 	}
Signature · 1 line
2.41.3
author
#14 lore raw

PATCH v3 8/8 xsk: don't support AF_XDP on 32-bit architectures

In reply to Jason Xing

From: Jason Xing <[email protected]>

In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a uintptr_t cast:

    skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);

On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned mode the chunk offset is encoded in bits 48-63 of the descriptor address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is lost entirely. The completion queue then returns a truncated address to userspace, making buffer recycling impossible.

Since we hear no one is using AF_XDP on 32-bit arch, we decided to strictly stop supporting it at compile time.

Fixes 0ebc27a4c67d ("xsk: avoid data corruption on cq descriptor number")
---
 net/xdp/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/xdp/Kconfig +1 -1
index 71af2febe72a..819aa5795f50 100644
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config XDP_SOCKETS
 	bool "XDP sockets"
-	depends on BPF_SYSCALL
+	depends on BPF_SYSCALL && 64BIT
 	default n
 	help
 	  XDP sockets allows a channel between XDP programs and
Signature · 1 line
2.41.3
#15 lore raw

Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures

From: Jason Xing <[email protected]>
Date: Wed, 22 Apr 2026 11:36:50 +0800

Quoted 6 lines

From: Jason Xing <[email protected]>

In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a uintptr_t cast:

Quoted 8 lines
    skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);

On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned mode the chunk offset is encoded in bits 48-63 of the descriptor address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is lost entirely. The completion queue then returns a truncated address to userspace, making buffer recycling impossible.

What if we relax the restriction a bit? For example, refuse to configure an XSk socket in unaligned mode if on a 32-bit arch? Or add a check under CONFIG_32_BIT like it was done in Page Pool:

skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);

#ifdef CONFIG_32BIT
if (((uintptr_t)skb_shinfo(skb)->destructor_arg) & ~0x1UL) != addr)

	// WARN_ONCE or whatever + error path

#endif

I never used XSk on a 32-bit arch, but back when I was working on 32-bit MIPS 1G routers, I wanted to add native XSk support to the Eth driver. Sure, just for fun, now that we have cheap AArch64 and other 64-bit embedded chips, 32-bit embedded networking SoCs are almost dead, but OTOH, as you can see, other subsystems like PP still try to support 32 bit. Especially given that this issue applies to only to the skb XSk path, not native in-driver implementations.

Quoted 17 lines

Since we hear no one is using AF_XDP on 32-bit arch, we decided to strictly stop supporting it at compile time.

Fixes 0ebc27a4c67d ("xsk: avoid data corruption on cq descriptor number")
---
 net/xdp/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/xdp/Kconfig +0 -0
index 71af2febe72a..819aa5795f50 100644

net/xdp/Kconfig line 7

Quoted 8 lines
net/xdp/Kconfig +1 -1
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config XDP_SOCKETS
 	bool "XDP sockets"
-	depends on BPF_SYSCALL
+	depends on BPF_SYSCALL && 64BIT
 	default n
 	help
 	  XDP sockets allows a channel between XDP programs and

Thanks,
Olek

author
#16 lore raw

Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures

Quoted 12 lines · Alexander Lobakin

From: Jason Xing <[email protected]>
Date: Wed, 22 Apr 2026 11:36:50 +0800

Quoted 8 lines

From: Jason Xing <[email protected]>

In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a uintptr_t cast:

    skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
Quoted 8 lines
Quoted 6 lines

On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned mode the chunk offset is encoded in bits 48-63 of the descriptor address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is lost entirely. The completion queue then returns a truncated address to userspace, making buffer recycling impossible.

What if we relax the restriction a bit? For example, refuse to configure

As to the bug itself, yes, It only affects the unaligned mode.

I wonder if we can support this after someone requires us to support 32-bit arch and use it in the real world, then we can use the previous patch to complete the full support (which doesn't harm the path on 64-bit arch).

The code looks like this based on your suggestion. Just for the record.

net/xdp/xdp_umem.c +0 -0
index 58da2f4f4397..03417b04592f 100644
@@ -177,6 +177,9 @@ static int xdp_umem_reg(struct xdp_umem *umem,

struct xdp_umem_reg *mr)
if (mr->flags & ~XDP_UMEM_FLAGS_VALID) return -EINVAL;
+ if (!IS_ENABLED(CONFIG_64BIT) && unaligned_chunks)
+ return -EOPNOTSUPP;
+
if (!unaligned_chunks && !is_power_of_2(chunk_size)) return -EINVAL;

Actually I'm fine with either of them. Right now I'm not so sure which direction this patch should take :)

Thanks,
Jason

Quoted 46 lines

an XSk socket in unaligned mode if on a 32-bit arch? Or add a check under CONFIG_32_BIT like it was done in Page Pool:

skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);

#ifdef CONFIG_32BIT
if (((uintptr_t)skb_shinfo(skb)->destructor_arg) & ~0x1UL) != addr)

        // WARN_ONCE or whatever + error path

#endif

I never used XSk on a 32-bit arch, but back when I was working on 32-bit MIPS 1G routers, I wanted to add native XSk support to the Eth driver. Sure, just for fun, now that we have cheap AArch64 and other 64-bit embedded chips, 32-bit embedded networking SoCs are almost dead, but OTOH, as you can see, other subsystems like PP still try to support 32 bit. Especially given that this issue applies to only to the skb XSk path, not native in-driver implementations.

Quoted 17 lines

Since we hear no one is using AF_XDP on 32-bit arch, we decided to strictly stop supporting it at compile time.

Fixes 0ebc27a4c67d ("xsk: avoid data corruption on cq descriptor number")
---
 net/xdp/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/xdp/Kconfig +0 -0
index 71af2febe72a..819aa5795f50 100644

net/xdp/Kconfig line 7

Quoted 8 lines
net/xdp/Kconfig +1 -1
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config XDP_SOCKETS
      bool "XDP sockets"
-     depends on BPF_SYSCALL
+     depends on BPF_SYSCALL && 64BIT
      default n
      help
        XDP sockets allows a channel between XDP programs and

Thanks,
Olek

#17 lore raw

Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures

Quoted 45 lines · Jason Xing
Quoted 12 lines · Alexander Lobakin

From: Jason Xing <[email protected]>
Date: Wed, 22 Apr 2026 11:36:50 +0800

Quoted 8 lines

From: Jason Xing <[email protected]>

In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a uintptr_t cast:

    skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
Quoted 8 lines
Quoted 6 lines

On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned mode the chunk offset is encoded in bits 48-63 of the descriptor address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is lost entirely. The completion queue then returns a truncated address to userspace, making buffer recycling impossible.

What if we relax the restriction a bit? For example, refuse to configure

As to the bug itself, yes, It only affects the unaligned mode.

I wonder if we can support this after someone requires us to support 32-bit arch and use it in the real world, then we can use the previous patch to complete the full support (which doesn't harm the path on 64-bit arch).

The code looks like this based on your suggestion. Just for the record.

net/xdp/xdp_umem.c +3 -0
index 58da2f4f4397..03417b04592f 100644
@@ -177,6 +177,9 @@ static int xdp_umem_reg(struct xdp_umem *umem,
 struct xdp_umem_reg *mr)
 if (mr->flags & ~XDP_UMEM_FLAGS_VALID)
 return -EINVAL;
+ if (!IS_ENABLED(CONFIG_64BIT) && unaligned_chunks)
+ return -EOPNOTSUPP;
+
 if (!unaligned_chunks && !is_power_of_2(chunk_size))
 return -EINVAL;
 
Quoted 2 lines

Actually I'm fine with either of them. Right now I'm not so sure which direction this patch should take :)

Abstracting from discussion, that is not a patch we would want against -net. We should not forbid 32bit on stable kernels, maybe there is someone in the 'basement' using 32bit xsk on stable kernel. Plus the fixes tag vs patch's content looks weird ;)

Quoted 50 lines

Thanks,
Jason

Quoted 46 lines

an XSk socket in unaligned mode if on a 32-bit arch? Or add a check under CONFIG_32_BIT like it was done in Page Pool:

skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);

#ifdef CONFIG_32BIT
if (((uintptr_t)skb_shinfo(skb)->destructor_arg) & ~0x1UL) != addr)

        // WARN_ONCE or whatever + error path

#endif

I never used XSk on a 32-bit arch, but back when I was working on 32-bit MIPS 1G routers, I wanted to add native XSk support to the Eth driver. Sure, just for fun, now that we have cheap AArch64 and other 64-bit embedded chips, 32-bit embedded networking SoCs are almost dead, but OTOH, as you can see, other subsystems like PP still try to support 32 bit. Especially given that this issue applies to only to the skb XSk path, not native in-driver implementations.

Quoted 17 lines

Since we hear no one is using AF_XDP on 32-bit arch, we decided to strictly stop supporting it at compile time.

Fixes 0ebc27a4c67d ("xsk: avoid data corruption on cq descriptor number")
---
 net/xdp/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/xdp/Kconfig +0 -0
index 71af2febe72a..819aa5795f50 100644

net/xdp/Kconfig line 7

Quoted 8 lines
net/xdp/Kconfig +1 -1
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config XDP_SOCKETS
      bool "XDP sockets"
-     depends on BPF_SYSCALL
+     depends on BPF_SYSCALL && 64BIT
      default n
      help
        XDP sockets allows a channel between XDP programs and

Thanks,
Olek

author
#18 lore raw

Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures

Quoted 47 lines · Maciej Fijalkowski
Quoted 45 lines · Jason Xing
Quoted 12 lines · Alexander Lobakin

From: Jason Xing <[email protected]>
Date: Wed, 22 Apr 2026 11:36:50 +0800

Quoted 8 lines

From: Jason Xing <[email protected]>

In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a uintptr_t cast:

    skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
Quoted 8 lines
Quoted 6 lines

On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned mode the chunk offset is encoded in bits 48-63 of the descriptor address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is lost entirely. The completion queue then returns a truncated address to userspace, making buffer recycling impossible.

What if we relax the restriction a bit? For example, refuse to configure

As to the bug itself, yes, It only affects the unaligned mode.

I wonder if we can support this after someone requires us to support 32-bit arch and use it in the real world, then we can use the previous patch to complete the full support (which doesn't harm the path on 64-bit arch).

The code looks like this based on your suggestion. Just for the record.

net/xdp/xdp_umem.c +3 -0
index 58da2f4f4397..03417b04592f 100644
@@ -177,6 +177,9 @@ static int xdp_umem_reg(struct xdp_umem *umem,
 struct xdp_umem_reg *mr)
 if (mr->flags & ~XDP_UMEM_FLAGS_VALID)
 return -EINVAL;
+ if (!IS_ENABLED(CONFIG_64BIT) && unaligned_chunks)
+ return -EOPNOTSUPP;
+
 if (!unaligned_chunks && !is_power_of_2(chunk_size))
 return -EINVAL;
 
Quoted 7 lines
Quoted 2 lines

Actually I'm fine with either of them. Right now I'm not so sure which direction this patch should take :)

Abstracting from discussion, that is not a patch we would want against -net. We should not forbid 32bit on stable kernels, maybe there is someone in the 'basement' using 32bit xsk on stable kernel. Plus the fixes tag vs patch's content looks weird ;)

Sorry, I don't follow this. Any specific suggestions on how to move forward?

Now we have three approaches w/o the fixes tag:
1. completely forbid 32-bit arch
2. only forbid unaligned mode on 32-bit arch
3. completely fix/modify the allocation logic of the first frag on 32-bit (see the v2-0008 patch)

I'm wondering how to proceed with that?

Thanks,
Jason

Quoted 51 lines
Quoted 51 lines

Thanks,
Jason

Quoted 46 lines

an XSk socket in unaligned mode if on a 32-bit arch? Or add a check under CONFIG_32_BIT like it was done in Page Pool:

skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);

#ifdef CONFIG_32BIT
if (((uintptr_t)skb_shinfo(skb)->destructor_arg) & ~0x1UL) != addr)

        // WARN_ONCE or whatever + error path

#endif

I never used XSk on a 32-bit arch, but back when I was working on 32-bit MIPS 1G routers, I wanted to add native XSk support to the Eth driver. Sure, just for fun, now that we have cheap AArch64 and other 64-bit embedded chips, 32-bit embedded networking SoCs are almost dead, but OTOH, as you can see, other subsystems like PP still try to support 32 bit. Especially given that this issue applies to only to the skb XSk path, not native in-driver implementations.

Quoted 17 lines

Since we hear no one is using AF_XDP on 32-bit arch, we decided to strictly stop supporting it at compile time.

Fixes 0ebc27a4c67d ("xsk: avoid data corruption on cq descriptor number")
---
 net/xdp/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/xdp/Kconfig +0 -0
index 71af2febe72a..819aa5795f50 100644

net/xdp/Kconfig line 7

Quoted 8 lines
net/xdp/Kconfig +1 -1
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config XDP_SOCKETS
      bool "XDP sockets"
-     depends on BPF_SYSCALL
+     depends on BPF_SYSCALL && 64BIT
      default n
      help
        XDP sockets allows a channel between XDP programs and

Thanks,
Olek

#19 lore raw

Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures

From: Jason Xing <[email protected]>
Date: Thu, 23 Apr 2026 07:49:58 +0800

Quoted 57 lines
Quoted 47 lines · Maciej Fijalkowski
Quoted 45 lines · Jason Xing
Quoted 12 lines · Alexander Lobakin

From: Jason Xing <[email protected]>
Date: Wed, 22 Apr 2026 11:36:50 +0800

Quoted 8 lines

From: Jason Xing <[email protected]>

In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a uintptr_t cast:

    skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
Quoted 8 lines
Quoted 6 lines

On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned mode the chunk offset is encoded in bits 48-63 of the descriptor address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is lost entirely. The completion queue then returns a truncated address to userspace, making buffer recycling impossible.

What if we relax the restriction a bit? For example, refuse to configure

As to the bug itself, yes, It only affects the unaligned mode.

I wonder if we can support this after someone requires us to support 32-bit arch and use it in the real world, then we can use the previous patch to complete the full support (which doesn't harm the path on 64-bit arch).

The code looks like this based on your suggestion. Just for the record.

net/xdp/xdp_umem.c +3 -0
index 58da2f4f4397..03417b04592f 100644
@@ -177,6 +177,9 @@ static int xdp_umem_reg(struct xdp_umem *umem,
 struct xdp_umem_reg *mr)
 if (mr->flags & ~XDP_UMEM_FLAGS_VALID)
 return -EINVAL;
+ if (!IS_ENABLED(CONFIG_64BIT) && unaligned_chunks)
+ return -EOPNOTSUPP;
+
 if (!unaligned_chunks && !is_power_of_2(chunk_size))
 return -EINVAL;
 
Quoted 7 lines
Quoted 2 lines

Actually I'm fine with either of them. Right now I'm not so sure which direction this patch should take :)

Abstracting from discussion, that is not a patch we would want against -net. We should not forbid 32bit on stable kernels, maybe there is someone in the 'basement' using 32bit xsk on stable kernel. Plus the fixes tag vs patch's content looks weird ;)

Quoted 1 line

Sorry, I don't follow this. Any specific suggestions on how to move forward?

Maciej just says that if we go the first route (forbid 32-bit arches), then this commit should not go to the stable/LTS kernels. But

Quoted 8 lines

Now we have three approaches w/o the fixes tag:
1. completely forbid 32-bit arch
2. only forbid unaligned mode on 32-bit arch
3. completely fix/modify the allocation logic of the first frag on 32-bit (see the v2-0008 patch)

I'm wondering how to proceed with that?

I've just reread the v2 thread (I realized I missed it earlier) and to me, the v2-0008 (route 3) solution looks good. It introduces a bit of overhead, but only for 32-bit arches, where the XSk performance will never be push-it-to-the-limit anyway (esp. given that this is skb mode).

So I'd personally vote for number 3. 47 insertions(+), 7 deletions(-) is not a big deal. And for 64-bit arches, there won't be any object code changes at all (unconditional return 0 and checking the return value of xsk_skb_init_misc() will be optimized out by the compiler).

Number 2 is also okay-ish, if we're sure that addr is always 32-bit on 32-bit arches in the aligned mode. But given that you already has a proper fix (n. 3) which allows to not forbid anything at all, I believe it's more preferred to fix rather than disable.

+ as mentioned above, if you go route number 3, the series could be sent to -net and the fix would eventually hit the stable/LTS trees, but I don't think we can disable/forbid something in the stable kernels.

Quoted 3 lines

Thanks,
Jason

Thanks,
Olek

author
#20 lore raw

Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures

Quoted 86 lines · Alexander Lobakin

From: Jason Xing <[email protected]>
Date: Thu, 23 Apr 2026 07:49:58 +0800

Quoted 57 lines
Quoted 47 lines · Maciej Fijalkowski
Quoted 45 lines · Jason Xing
Quoted 12 lines · Alexander Lobakin

From: Jason Xing <[email protected]>
Date: Wed, 22 Apr 2026 11:36:50 +0800

Quoted 8 lines

From: Jason Xing <[email protected]>

In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a uintptr_t cast:

    skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
Quoted 8 lines
Quoted 6 lines

On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned mode the chunk offset is encoded in bits 48-63 of the descriptor address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is lost entirely. The completion queue then returns a truncated address to userspace, making buffer recycling impossible.

What if we relax the restriction a bit? For example, refuse to configure

As to the bug itself, yes, It only affects the unaligned mode.

I wonder if we can support this after someone requires us to support 32-bit arch and use it in the real world, then we can use the previous patch to complete the full support (which doesn't harm the path on 64-bit arch).

The code looks like this based on your suggestion. Just for the record.

net/xdp/xdp_umem.c +3 -0
index 58da2f4f4397..03417b04592f 100644
@@ -177,6 +177,9 @@ static int xdp_umem_reg(struct xdp_umem *umem,
 struct xdp_umem_reg *mr)
 if (mr->flags & ~XDP_UMEM_FLAGS_VALID)
 return -EINVAL;
+ if (!IS_ENABLED(CONFIG_64BIT) && unaligned_chunks)
+ return -EOPNOTSUPP;
+
 if (!unaligned_chunks && !is_power_of_2(chunk_size))
 return -EINVAL;
 
Quoted 7 lines
Quoted 2 lines

Actually I'm fine with either of them. Right now I'm not so sure which direction this patch should take :)

Abstracting from discussion, that is not a patch we would want against -net. We should not forbid 32bit on stable kernels, maybe there is someone in the 'basement' using 32bit xsk on stable kernel. Plus the fixes tag vs patch's content looks weird ;)

Quoted 1 line

Sorry, I don't follow this. Any specific suggestions on how to move forward?

Maciej just says that if we go the first route (forbid 32-bit arches), then this commit should not go to the stable/LTS kernels. But

Quoted 8 lines

Now we have three approaches w/o the fixes tag:
1. completely forbid 32-bit arch
2. only forbid unaligned mode on 32-bit arch
3. completely fix/modify the allocation logic of the first frag on 32-bit (see the v2-0008 patch)

I'm wondering how to proceed with that?

I've just reread the v2 thread (I realized I missed it earlier) and to me, the v2-0008 (route 3) solution looks good. It introduces a bit of overhead, but only for 32-bit arches, where the XSk performance will never be push-it-to-the-limit anyway (esp. given that this is skb mode).

So I'd personally vote for number 3. 47 insertions(+), 7 deletions(-) is not a big deal. And for 64-bit arches, there won't be any object code changes at all (unconditional return 0 and checking the return value of xsk_skb_init_misc() will be optimized out by the compiler).

Quoted 8 lines

Number 2 is also okay-ish, if we're sure that addr is always 32-bit on 32-bit arches in the aligned mode. But given that you already has a proper fix (n. 3) which allows to not forbid anything at all, I believe it's more preferred to fix rather than disable.

+ as mentioned above, if you go route number 3, the series could be sent to -net and the fix would eventually hit the stable/LTS trees, but I don't think we can disable/forbid something in the stable kernels.

Thanks for the explanation!

I will submit solution #3 to fix this issue. And the v4-0008 should be simpler. We'll see :)

Thanks,
Jason

#21 lore raw

Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures

In reply to Jason Xing

From: Jason Xing <[email protected]>
Date: Thu, 23 Apr 2026 00:37:07 +0800

Quoted 36 lines
Quoted 12 lines · Alexander Lobakin

From: Jason Xing <[email protected]>
Date: Wed, 22 Apr 2026 11:36:50 +0800

Quoted 8 lines

From: Jason Xing <[email protected]>

In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a uintptr_t cast:

    skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
Quoted 8 lines
Quoted 6 lines

On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned mode the chunk offset is encoded in bits 48-63 of the descriptor address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is lost entirely. The completion queue then returns a truncated address to userspace, making buffer recycling impossible.

What if we relax the restriction a bit? For example, refuse to configure

As to the bug itself, yes, It only affects the unaligned mode.

I wonder if we can support this after someone requires us to support 32-bit arch and use it in the real world, then we can use the previous patch to complete the full support (which doesn't harm the path on 64-bit arch).

The code looks like this based on your suggestion. Just for the record.

net/xdp/xdp_umem.c +0 -0
index 58da2f4f4397..03417b04592f 100644

net/xdp/xdp_umem.c static int xdp_umem_reg(struct xdp_umem *umem, · line 181

Quoted 5 lines
net/xdp/xdp_umem.c +2 -0
@@ -177,6 +177,9 @@ static int xdp_umem_reg(struct xdp_umem *umem,
 struct xdp_umem_reg *mr)
 if (mr->flags & ~XDP_UMEM_FLAGS_VALID)
 return -EINVAL;
+ if (!IS_ENABLED(CONFIG_64BIT) && unaligned_chunks)
+ return -EOPNOTSUPP;

I'm fine with this one, as well as with the check I proposed. This one would happen during the configuration so it won't affect the hotpath, my check is just inspired by what Page Pool does when sizeof(dma_addr_t) > sizeof(unsigned long) (rare case but perfectly valid on some architectures).

Quoted 6 lines
net/xdp/xdp_umem.c +1 -0
+
 if (!unaligned_chunks && !is_power_of_2(chunk_size))
 return -EINVAL;
 

Actually I'm fine with either of them. Right now I'm not so sure which direction this patch should take :)

Up to you and other reviewers, but I really do think cutting off 32-bit arches entirely due to a bug which can happen only under certain conditions (skb XSk mode + unaligned mode) is too aggressive.

Quoted 25 lines

Thanks,
Jason

Quoted 21 lines

an XSk socket in unaligned mode if on a 32-bit arch? Or add a check under CONFIG_32_BIT like it was done in Page Pool:

skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);

#ifdef CONFIG_32BIT
if (((uintptr_t)skb_shinfo(skb)->destructor_arg) & ~0x1UL) != addr)

        // WARN_ONCE or whatever + error path

#endif

I never used XSk on a 32-bit arch, but back when I was working on 32-bit MIPS 1G routers, I wanted to add native XSk support to the Eth driver. Sure, just for fun, now that we have cheap AArch64 and other 64-bit embedded chips, 32-bit embedded networking SoCs are almost dead, but OTOH, as you can see, other subsystems like PP still try to support 32 bit. Especially given that this issue applies to only to the skb XSk path, not native in-driver implementations.

Quoted 3 lines

Since we hear no one is using AF_XDP on 32-bit arch, we decided to strictly stop supporting it at compile time.

Thanks,
Olek

Acked-by on v3 8/8
#22 lore raw

Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures

In reply to Jason Xing
Quoted 40 lines

From: Jason Xing <[email protected]>

In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a uintptr_t cast:

    skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);

On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned mode the chunk offset is encoded in bits 48-63 of the descriptor address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is lost entirely. The completion queue then returns a truncated address to userspace, making buffer recycling impossible.

Since we hear no one is using AF_XDP on 32-bit arch, we decided to strictly stop supporting it at compile time.

Fixes 0ebc27a4c67d ("xsk: avoid data corruption on cq descriptor number")
---
 net/xdp/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/xdp/Kconfig +1 -2
index 71af2febe72a..819aa5795f50 100644
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config XDP_SOCKETS
 	bool "XDP sockets"
-	depends on BPF_SYSCALL
+	depends on BPF_SYSCALL && 64BIT
 	default n
 	help
 	  XDP sockets allows a channel between XDP programs and
--
Quoted 2 lines

2.41.3

#23 lore raw

Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures

In reply to Jason Xing
Quoted 6 lines · On Wed, 22 Apr 2026 11:36:50 +0800 Jason Xing <[email protected]> wrote:

From: Jason Xing <[email protected]>

In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a uintptr_t cast:

Quoted 8 lines
    skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);

On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned mode the chunk offset is encoded in bits 48-63 of the descriptor address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is lost entirely. The completion queue then returns a truncated address to userspace, making buffer recycling impossible.

I had a look at how this is used.

I suspect that XSK_UNALIGNED_BUF_OFFSET_SHIFT can just be made smaller. The 'addr' isn't really a normal address of any kind, it is a really and offset into an array made up of pages of memory.
The actual address (kernel virtual of dma) is generated by:

	ptr->array[addr >> PAGE_SHIFT] + addr & ~PAGE_MASK

(after removing the unaligned chunk offset from 'addr').

It is actually quite likely that there are enough free high bits even in 32bit mode.

David

Quoted 25 lines

Since we hear no one is using AF_XDP on 32-bit arch, we decided to strictly stop supporting it at compile time.

Fixes 0ebc27a4c67d ("xsk: avoid data corruption on cq descriptor number")
---
 net/xdp/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/xdp/Kconfig +1 -1
index 71af2febe72a..819aa5795f50 100644
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config XDP_SOCKETS
 	bool "XDP sockets"
-	depends on BPF_SYSCALL
+	depends on BPF_SYSCALL && 64BIT
 	default n
 	help
 	  XDP sockets allows a channel between XDP programs and
author
#24 lore raw

Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures

Quoted 25 lines · David Laight
Quoted 6 lines · On Wed, 22 Apr 2026 11:36:50 +0800 Jason Xing <[email protected]> wrote:

From: Jason Xing <[email protected]>

In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a uintptr_t cast:

Quoted 8 lines
    skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);

On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned mode the chunk offset is encoded in bits 48-63 of the descriptor address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is lost entirely. The completion queue then returns a truncated address to userspace, making buffer recycling impossible.

I had a look at how this is used.

I suspect that XSK_UNALIGNED_BUF_OFFSET_SHIFT can just be made smaller. The 'addr' isn't really a normal address of any kind, it is a really and offset into an array made up of pages of memory.
The actual address (kernel virtual of dma) is generated by:

Quoted 5 lines
        ptr->array[addr >> PAGE_SHIFT] + addr & ~PAGE_MASK

(after removing the unaligned chunk offset from 'addr').

It is actually quite likely that there are enough free high bits even in 32bit mode.

Interesting idea. But if everything is folded into a 32-bit area, it means the size of umem is only 20-bit. 20-bit reflects that users only put a max 1MB data into it. It might be too small to use?

Thanks,
Jason

Quoted 29 lines

David

Quoted 25 lines

Since we hear no one is using AF_XDP on 32-bit arch, we decided to strictly stop supporting it at compile time.

Fixes 0ebc27a4c67d ("xsk: avoid data corruption on cq descriptor number")
---
 net/xdp/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/xdp/Kconfig +1 -1
index 71af2febe72a..819aa5795f50 100644
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config XDP_SOCKETS
      bool "XDP sockets"
-     depends on BPF_SYSCALL
+     depends on BPF_SYSCALL && 64BIT
      default n
      help
        XDP sockets allows a channel between XDP programs and