xsk: fix bugs around xsk skb allocation
Subsystems (4)
cover xsk: fix bugs around xsk skb allocation
From: Jason Xing <[email protected]>
There are four extremely rare issues around xsk_build_skb(). Two of them were founded by Sashiko[1].
[1]: https://lore.kernel.org/all/[email protected]/
Jason Xing (4):
xsk: avoid skb leak in XDP_TX_METADATA case
xsk: free the skb when hitting the upper bound MAX_SKB_FRAGS
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
net/xdp/xsk.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-)
Signature · 1 line
2.41.3
PATCH 1/4 xsk: avoid skb leak in XDP_TX_METADATA case
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.
--- net/xdp/xsk.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
net/xdp/xsk.c +3 -1
@@ -743,8 +743,10 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,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)) +if (unlikely (err)) { +kfree_skb (skb);return ERR_PTR (err); + } } }else {struct xsk_addrs *xsk_addr;
Signature · 1 line
2.41.3
[email protected]
AI reviewRe: [PATCH net 1/4] xsk: avoid skb leak in XDP_TX_METADATA case
Sashiko AI review found 2 potential issue(s): - [High] Premature SKB free triggers AF_XDP completion destructor, corrupting the Completion Queue (CQ) and causing an infinite TX loop. - [High]…
Sashiko AI review found 2 potential issue(s):
- [High] Premature SKB free triggers AF_XDP completion destructor, corrupting the Completion Queue (CQ) and causing an infinite TX loop.
- [High] Calling xsk_skb_metadata() before SKB fragments are attached breaks software checksum offload and permanently wedges the TX ring. --
commit 7fb48e575c16e2e098969186062c6a31c8245353
Author: Jason Xing <[email protected]>
xsk: avoid skb leak in XDP_TX_METADATA case
This commit prevents a memory leak when xsk_skb_metadata() fails by explicitly freeing the skb before returning to the caller.
net/xdp/xsk.c static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, · line 825
Quoted 8 lines
net/xdp/xsk.c +0
-0@@ -823,8 +823,10 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,xsk_skb_init_misc (skb, xs, desc->addr);if (desc->options &XDP_TX_METADATA ) { err =xsk_skb_metadata (skb, buffer, desc, pool, hr);
Is it safe to call xsk_skb_metadata() here before the SKB fragments are attached?
Because the fragments are populated later in the function via skb_fill_page_desc(), both skb->len and skb_headlen(skb) are 0 at this point.
If XDP_TXMD_FLAGS_CHECKSUM is requested and software fallback is active, xsk_skb_metadata() will call skb_checksum_help(). Because skb_headlen is 0, the bounds check in skb_checksum_help() can trigger a WARN_ONCE and return -EINVAL. This permanently wedges the TX ring since -EINVAL does not consume the descriptor.
net/xdp/xsk.c static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, · line 747
Quoted 3 lines
net/xdp/xsk.c +2
-1-if (unlikely (err)) +if (unlikely (err)) { +kfree_skb (skb);
Does freeing the SKB here cause issues with the completion queue?
Just before this block, xsk_skb_init_misc() sets skb->destructor to xsk_destruct_skb. Calling kfree_skb() triggers this destructor, which submits the descriptor's address to the CQ and increments the producer index.
However, the caller xsk_build_skb() will see the error and call xsk_cq_cancel_locked(), which decrements the cached producer index. This desynchronizes the queue reservation pointers.
Additionally, because the error is not -EOVERFLOW, the kernel does not advance the TX ring consumer pointer. The bad descriptor remains in the TX ring and will be retried continuously, potentially causing an infinite loop.
Should skb->destructor be restored to sock_wfree before calling kfree_skb(), or should xsk_skb_init_misc() be deferred until after the metadata is validated?
Quoted 3 lines
net/xdp/xsk.c +1
-0return ERR_PTR (err); + } }
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Re: [PATCH net 1/4] xsk: avoid skb leak in XDP_TX_METADATA case
Quoted 28 lines · On Sun, Apr 19, 2026 at 12:58 PM <[email protected]> wrote:
Sashiko AI review found 2 potential issue(s):
- [High] Premature SKB free triggersAF_XDPcompletion destructor, corrupting the Completion Queue (CQ) and causing an infinite TX loop.
- [High] Callingxsk_skb_metadata()before SKB fragments are attached breaks software checksum offload and permanently wedges the TX ring. --commit
7fb48e575c16e2e098969186062c6a31c8245353
Author: Jason Xing <[email protected]>xsk: avoid skb leak in
XDP_TX_METADATAcaseThis commit prevents a memory leak when
xsk_skb_metadata()fails by explicitly freeing the skb before returning to the caller.net/xdp/xsk.c static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, · line 825
Quoted 8 lines
net/xdp/xsk.c +0
-0@@ -823,8 +823,10 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,xsk_skb_init_misc (skb, xs, desc->addr);if (desc->options &XDP_TX_METADATA ) { err =xsk_skb_metadata (skb, buffer, desc, pool, hr);Is it safe to call
xsk_skb_metadata()here before the SKB fragments are attached?
Quoted 8 lines
Because the fragments are populated later in the function via
skb_fill_page_desc(), bothskb->lenandskb_headlen(skb)are 0 at this point.If
XDP_TXMD_FLAGS_CHECKSUMis requested and software fallback is active,xsk_skb_metadata()will callskb_checksum_help(). Becauseskb_headlenis 0, the bounds check inskb_checksum_help()can trigger aWARN_ONCEand return-EINVAL. This permanently wedges the TX ring since-EINVALdoes not consume the descriptor.
I must state that this problem is not introduced by the series. The following commit should be fixed soon:
commit 30c3055f9c0d84a67b8fd723bdec9b1b52b3c695
Author: Maciej Fijalkowski <[email protected]> Date: Thu Sep 25 18:00:09 2025 +0200
xsk: wrap generic metadata handling onto separate function
xsk_build_skb() has gone wild with its size and one of the things we can do about it is to pull out a branch that takes care of metadata handling and make it a separate function.
While at it, let us add metadata SW support for devices supporting IFF_TX_SKB_NO_LINEAR flag, that happen to have separate logic for building skb in xsk's generic xmit path.
I'll consider folding it into the next version even though it's a bit complex to handle :(
Quoted 15 lines
net/xdp/xsk.c static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, · line 747
Quoted 4 lines
net/xdp/xsk.c +2
-1-if (unlikely (err)) +if (unlikely (err)) { +kfree_skb (skb);Does freeing the SKB here cause issues with the completion queue?
Just before this block,
xsk_skb_init_misc()setsskb->destructortoxsk_destruct_skb. Callingkfree_skb()triggers this destructor, which submits the descriptor's address to the CQ and increments the producer index.However, the caller
xsk_build_skb()will see the error and callxsk_cq_cancel_locked(), which decrements the cached producer index. This desynchronizes the queue reservation pointers.
Quoted 7 lines
Additionally, because the error is not
-EOVERFLOW, the kernel does not advance the TX ring consumer pointer. The bad descriptor remains in the TX ring and will be retried continuously, potentially causing an infinite loop.Should
skb->destructorbe restored tosock_wfreebefore callingkfree_skb(), or shouldxsk_skb_init_misc()be deferred until after the metadata is validated?
Amazing really. The same here. Sashiko checked this pre-existing behavior is buggy, which the patch 1 only does follow.
In terms of the free_err logic in xsk_build_skb, the untransmitted skb should be freed by the common new helper:
static void xsk_drop_untransmitted_skb(struct sk_buff *skb)
{
skb->destructor = sock_wfree;
kfree_skb(skb);
}Will cook it as a standalone patch to fix the commit:
commit c30d084960cf316c95fbf145d39974ce1ff7889c
Author: Maciej Fijalkowski <[email protected]> Date: Thu Sep 25 18:00:07 2025 +0200
xsk: avoid overwriting skb fields for multi-buffer traffic
We are unnecessarily setting a bunch of skb fields per each processed descriptor, which is redundant for fragmented frames.
Let us set these respective members for first fragment only. To address both paths that we have within xsk_build_skb(), move assignments onto xsk_set_destructor_arg() and rename it to xsk_skb_init_misc().
Thanks,
Jason
Quoted 7 lines
net/xdp/xsk.c static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, · line 750
Quoted 4 lines
net/xdp/xsk.c +1
-0return ERR_PTR (err); + } }--
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Re: [PATCH net 1/4] xsk: avoid skb leak in XDP_TX_METADATA case
In reply to Jason XingQuoted 12 lines · 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 andxs->skbis NULL) and users enable metadata feature.
2.xsk_skb_metadata()returns a error code.
3. the callerxsk_build_skb()clears skb by using 'skb = NULL;'.
4. there is no chance to free this skb anymore.
Quoted 3 lines
Fixes 30c3055f9c0d ("xsk: wrap generic metadata handling onto separate function")Signed-off-by Jason Xing <[email protected]>
Re: [PATCH net 1/4] xsk: avoid skb leak in XDP_TX_METADATA case
Quoted 13 lines · Stanislav Fomichev
Quoted 12 lines · 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 andxs->skbis NULL) and users enable metadata feature.
2.xsk_skb_metadata()returns a error code.
3. the callerxsk_build_skb()clears skb by using 'skb = NULL;'.
4. there is no chance to free this skb anymore.
Quoted 5 lines
Quoted 3 lines
Fixes 30c3055f9c0d ("xsk: wrap generic metadata handling onto separate function")Signed-off-by Jason Xing <[email protected]>
Actually, I take that back.. While looking at patch 2 (which always confuses me with that -EOVERFLOW handling), looks like we set skb->destructor in xsk_skb_init_misc? So this kfree will do xsk_cq_submit_addr_locked? Not sure that's what we want? Should we move xsk_skb_init_misc to happen after xsk_skb_metadata?
Re: [PATCH net 1/4] xsk: avoid skb leak in XDP_TX_METADATA case
Quoted 18 lines · Stanislav Fomichev
Quoted 17 lines · Stanislav Fomichev
Quoted 15 lines · 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 andxs->skbis NULL) and users enable metadata feature.
2.xsk_skb_metadata()returns a error code.
3. the callerxsk_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")Signed-off-by Jason Xing <[email protected]>
Quoted 7 lines
Quoted 1 line
Actually, I take that back.. While looking at patch 2 (which always confuses me with that
-EOVERFLOWhandling), looks like we setskb->destructorinxsk_skb_init_misc? So this kfree will doxsk_cq_submit_addr_locked? Not sure that's what we want? Should we movexsk_skb_init_miscto happen afterxsk_skb_metadata?
Ooops, looks like you already addressed these in v2? Let me look into that..
Re: [PATCH net 1/4] xsk: avoid skb leak in XDP_TX_METADATA case
Quoted 22 lines · Stanislav Fomichev
Quoted 20 lines · Stanislav Fomichev
Quoted 18 lines · Stanislav Fomichev
Quoted 12 lines · 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 andxs->skbis NULL) and users enable metadata feature.
2.xsk_skb_metadata()returns a error code.
3. the callerxsk_build_skb()clears skb by using 'skb = NULL;'.
4. there is no chance to free this skb anymore.Quoted 3 lines
Fixes 30c3055f9c0d ("xsk: wrap generic metadata handling onto separate function")Signed-off-by Jason Xing <[email protected]>
Quoted 8 lines
Quoted 5 lines
Actually, I take that back.. While looking at patch 2 (which always confuses me with that
-EOVERFLOWhandling), looks like we setskb->destructorinxsk_skb_init_misc? So this kfree will doxsk_cq_submit_addr_locked? Not sure that's what we want? Should we movexsk_skb_init_miscto happen afterxsk_skb_metadata?Ooops, looks like you already addressed these in v2? Let me look into that..
Thanks for the detailed check. To be frank, my mind sometimes went blank while checking those complex paths...
Yep, let's discuss that there instead :)
Thanks,
Jason
PATCH 2/4 xsk: free the skb when hitting the upper bound MAX_SKB_FRAGS
In reply to Jason XingFrom: 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.
--- net/xdp/xsk.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
net/xdp/xsk.c +4 -1
@@ -778,8 +778,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
[email protected]
AI reviewRe: [PATCH net 2/4] xsk: free the skb when hitting the upper bound MAX_SKB_FRAGS
Sashiko AI review found 2 potential issue(s): - [High] The patch leaves a guaranteed NULL pointer dereference in the caller's error handling path when `-EOVERFLOW` is returned for the first…
Sashiko AI review found 2 potential issue(s):
- [High] The patch leaves a guaranteed NULL pointer dereference in the caller's error handling path when -EOVERFLOW is returned for the first fragment.
- [High] Calling kfree_skb(skb) directly triggers the packet destructor and incorrectly submits the dropped packet to the userspace Completion Queue (CQ). --
commit e5bfaac263557abd44f47ec6e9c85e50c7aa1efc
Author: Jason Xing <[email protected]>
xsk: free the skb when hitting the upper bound MAX_SKB_FRAGS
This commit explicitly frees the skb when the MAX_SKB_FRAGS limit is hit on the first descriptor to avoid a memory leak.
net/xdp/xsk.c static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, · line 863
Quoted 12 lines
net/xdp/xsk.c +3
-1@@ -858,8 +858,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);
If we free the newly allocated skb here and return -EOVERFLOW, the caller xsk_build_skb() catches the error and jumps to free_err, where it calls:
if (err == -EOVERFLOW) {
/* Drop the packet */
xsk_inc_num_desc(xs->skb);Since xs->skb is still NULL, will this cause xsk_inc_num_desc() to dereference a NULL pointer?
Note that this regression appears to be fixed in a later commit 1c728000f884c ("xsk: handle NULL dereference of the skb without frags issue").
Additionally, does calling kfree_skb(skb) here trigger the packet destructor and incorrectly submit the dropped packet to the userspace completion queue?
Prior to entering the fragmentation loop, xsk_skb_init_misc() sets the destructor to xsk_destruct_skb(). When kfree_skb(skb) is called, it invokes this destructor, which sequentially calls xsk_cq_submit_addr_locked().
This writes the descriptor address to the completion queue and advances the user-visible q->ring->producer pointer, falsely notifying userspace of a successful transmission.
The caller xsk_build_skb() then executes its own error handling, calling xsk_cq_cancel_locked(), which only decrements the kernel's internal q->cached_prod counter.
Could this desynchronize the internal queue tracking variables from the shared memory ring pointers?
Should the destructor be changed back to sock_wfree before freeing it, similar to how xsk_consume_skb() handles it?
Quoted 3 lines
net/xdp/xsk.c +1
-0return ERR_PTR (-EOVERFLOW ); + }
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Re: [PATCH net 2/4] xsk: free the skb when hitting the upper bound MAX_SKB_FRAGS
Quoted 34 lines · On Sun, Apr 19, 2026 at 12:58 PM <[email protected]> wrote:
Sashiko AI review found 2 potential issue(s):
- [High] The patch leaves a guaranteed NULL pointer dereference in the caller's error handling path when-EOVERFLOWis returned for the first fragment.
- [High] Callingkfree_skb(skb)directly triggers the packet destructor and incorrectly submits the dropped packet to the userspace Completion Queue (CQ). --commit
e5bfaac263557abd44f47ec6e9c85e50c7aa1efc
Author: Jason Xing <[email protected]>xsk: free the skb when hitting the upper bound
MAX_SKB_FRAGSThis commit explicitly frees the skb when the
MAX_SKB_FRAGSlimit is hit on the first descriptor to avoid a memory leak.net/xdp/xsk.c static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, · line 863
Quoted 12 lines
net/xdp/xsk.c +3
-1@@ -858,8 +858,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);If we free the newly allocated skb here and return
-EOVERFLOW, the callerxsk_build_skb()catches the error and jumps tofree_err, where it calls:if (err == -EOVERFLOW) { /* Drop the packet */ xsk_inc_num_desc(xs->skb);
Quoted 5 lines
Since
xs->skbis still NULL, will this causexsk_inc_num_desc()to dereference a NULL pointer?Note that this regression appears to be fixed in a later commit
1c728000f884c("xsk: handle NULL dereference of the skb without frags issue").
As you noticed, it was already fixed by patch 3.
Quoted 16 lines
Additionally, does calling
kfree_skb(skb)here trigger the packet destructor and incorrectly submit the dropped packet to the userspace completion queue?Prior to entering the fragmentation loop,
xsk_skb_init_misc()sets the destructor toxsk_destruct_skb(). Whenkfree_skb(skb)is called, it invokes this destructor, which sequentially callsxsk_cq_submit_addr_locked().This writes the descriptor address to the completion queue and advances the user-visible
q->ring->producerpointer, falsely notifying userspace of a successful transmission.The caller
xsk_build_skb()then executes its own error handling, callingxsk_cq_cancel_locked(), which only decrements the kernel's internalq->cached_prodcounter.
Quoted 5 lines
Could this desynchronize the internal queue tracking variables from the shared memory ring pointers?
Should the destructor be changed back to
sock_wfreebefore freeing it, similar to howxsk_consume_skb()handles it?
Right, it's the same (pre-existing) bug as pointed out in patch 1.
Thanks,
Jason
Quoted 7 lines
net/xdp/xsk.c static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, · line 785
Quoted 4 lines
net/xdp/xsk.c +1
-0return ERR_PTR (-EOVERFLOW ); + }--
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Re: [PATCH net 2/4] xsk: free the skb when hitting the upper bound MAX_SKB_FRAGS
In reply to Jason XingQuoted 42 lines · 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 meansxs->skbis NULL) and hit the limitMAX_SKB_FRAGS.
2.xsk_build_skb_zerocopy()returns-EOVERFLOW.
3. the callerxsk_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->skbis not NULL,xsk_build_skb()will callxsk_drop_skb(xs->skb)to do the right thing.Fixes cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path")Signed-off-by Jason Xing <[email protected]>--- net/xdp/xsk.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)net/xdp/xsk.c +4
-1@@ -778,8 +778,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
As mentioned for patch 1, need to do something with that xsk_skb_init_misc... Otherwise we hit destructor with xsk_cq_submit_addr_locked here as well
PATCH 3/4 xsk: handle NULL dereference of the skb without frags issue
In reply to Jason XingFrom: 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 the patch 2/4, 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.
--- net/xdp/xsk.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
net/xdp/xsk.c +8 -3
@@ -898,9 +898,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
[email protected]
AI reviewRe: [PATCH net 3/4] xsk: handle NULL dereference of the skb without frags issue
Sashiko AI review found 2 potential issue(s): - [Critical] Calling `kfree_skb(skb)` without clearing the SKB destructor causes Completion Queue (CQ) ring corruption and false completions. - [High]…
Sashiko AI review found 2 potential issue(s):
- [Critical] Calling kfree_skb(skb) without clearing the SKB destructor causes Completion Queue (CQ) ring corruption and false completions.
- [High] Use-after-free of xs->skb in the continuation path when an error like -EAGAIN occurs.
--
commit 1c728000f884ce0a536d9c1fd763eb0c560ada51
Author: Jason Xing <[email protected]>
xsk: handle NULL dereference of the skb without frags issue
This commit addresses a NULL pointer dereference in the error path of xsk_build_skb_zerocopy() by checking if xs->skb is valid before dereferencing it during -EOVERFLOW handling. It also adds an alternative branch to handle bookkeeping for the first-descriptor case.
net/xdp/xsk.c static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, · line 978
Quoted 6 lines
net/xdp/xsk.c +0
-0@@ -978,9 +978,14 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,kfree_skb (skb);
Can this lead to a use-after-free on xs->skb?
In the non-zerocopy path, if the first descriptor is linear, nr_frags remains 0 and the caller __xsk_generic_xmit() stores the SKB in xs->skb.
During the next iteration for a continuation descriptor, if an error like -EAGAIN occurs (e.g., from alloc_page()), the code jumps to the free_err label. The cleanup logic if (skb && !skb_shinfo(skb)->nr_frags) would evaluate to true, freeing the entire SKB.
The function then returns -EAGAIN, leaving the freed SKB pointer in xs->skb. On the next application retry, the code could dereference the freed xs->skb.
Note that this regression is fixed later in the patch series by commit 'xsk:
fix use-after-free of xs->skb in xsk_build_skb() free_err path'.
Also, could calling kfree_skb(skb) without clearing the SKB destructor cause Completion Queue (CQ) ring corruption and false completions?
In xsk_build_skb_zerocopy(), xsk_skb_init_misc() sets skb->destructor = xsk_destruct_skb. When an error occurs on the first descriptor (e.g., xsk_skb_metadata() returning -EINVAL), kfree_skb(skb) is called.
Because the destructor is not overridden with sock_wfree (like xsk_consume_skb() does), kfree_skb(skb) triggers xsk_destruct_skb(), which submits the descriptor to the CQ and increments q->ring->producer.
Quoted 16 lines
net/xdp/xsk.c +8
-3if (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 */ xsk_cq_cancel_locked (xs->pool,1 );
Quoted 1 line
}
The error handling paths then call xsk_cq_cancel_locked(), which decrements the reservation counter q->cached_prod.
Does decrementing the reservation while the actual producer pointer has advanced break queue accounting?
The kernel calculates available space using cached_prod; this artificial underflow could cause the queue to over-report free space, allowing the producer to silently overwrite unread user-space CQ entries.
Furthermore, in the -EINVAL path, does the invalid descriptor generate a false completion to user space without being released from the TX ring? When the application reuses the completed buffer, could the kernel retransmit the same descriptor on retry, causing a use-after-free and data corruption?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
Re: [PATCH net 3/4] xsk: handle NULL dereference of the skb without frags issue
Quoted 25 lines · On Sun, Apr 19, 2026 at 12:58 PM <[email protected]> wrote:
Sashiko AI review found 2 potential issue(s):
- [Critical] Callingkfree_skb(skb)without clearing the SKB destructor causes Completion Queue (CQ) ring corruption and false completions.
- [High] Use-after-free ofxs->skbin the continuation path when an error like-EAGAINoccurs.
--commit
1c728000f884ce0a536d9c1fd763eb0c560ada51
Author: Jason Xing <[email protected]>xsk: handle NULL dereference of the skb without frags issue
This commit addresses a NULL pointer dereference in the error path of
xsk_build_skb_zerocopy()by checking ifxs->skbis valid before dereferencing it during-EOVERFLOWhandling. It also adds an alternative branch to handle bookkeeping for the first-descriptor case.net/xdp/xsk.c static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, · line 978
Quoted 6 lines
net/xdp/xsk.c +0
-0@@ -978,9 +978,14 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,kfree_skb (skb);Can this lead to a use-after-free on
xs->skb?
Quoted 2 lines
In the non-zerocopy path, if the first descriptor is linear,
nr_fragsremains 0 and the caller__xsk_generic_xmit()stores the SKB inxs->skb.
Patch 4 already handles this.
Quoted 48 lines
During the next iteration for a continuation descriptor, if an error like
-EAGAINoccurs (e.g., fromalloc_page()), the code jumps to thefree_errlabel. The cleanup logic if (skb && !skb_shinfo(skb)->nr_frags) would evaluate to true, freeing the entire SKB.The function then returns
-EAGAIN, leaving the freed SKB pointer inxs->skb. On the next application retry, the code could dereference the freedxs->skb.Note that this regression is fixed later in the patch series by commit 'xsk:
fix use-after-free ofxs->skbinxsk_build_skb()free_errpath'.Also, could calling
kfree_skb(skb)without clearing the SKB destructor cause Completion Queue (CQ) ring corruption and false completions?In
xsk_build_skb_zerocopy(),xsk_skb_init_misc()setsskb->destructor=xsk_destruct_skb. When an error occurs on the first descriptor (e.g.,xsk_skb_metadata()returning-EINVAL),kfree_skb(skb)is called.Because the destructor is not overridden with
sock_wfree(likexsk_consume_skb()does),kfree_skb(skb)triggersxsk_destruct_skb(), which submits the descriptor to the CQ and incrementsq->ring->producer.Quoted 16 lines
net/xdp/xsk.c +8
-3if (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 */ xsk_cq_cancel_locked (xs->pool,1 );Quoted 1 line
}The error handling paths then call
xsk_cq_cancel_locked(), which decrements the reservation counterq->cached_prod.Does decrementing the reservation while the actual producer pointer has advanced break queue accounting?
Quoted 8 lines
The kernel calculates available space using
cached_prod; this artificial underflow could cause the queue to over-report free space, allowing the producer to silently overwrite unread user-space CQ entries.Furthermore, in the
-EINVALpath, does the invalid descriptor generate a false completion to user space without being released from the TX ring? When the application reuses the completed buffer, could the kernel retransmit the same descriptor on retry, causing a use-after-free and data corruption?
For the above two issues, yes, we've discussed in the previous patch :)
Thanks,
Jason
Quoted 3 lines
--
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
PATCH 4/4 xsk: fix use-after-free of xs->skb in xsk_build_skb() free_err path
In reply to Jason XingFrom: 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 adding a !xs->skb check to the condition, 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.
--- net/xdp/xsk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/xdp/xsk.c +1 -1
@@ -894,7 +894,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 && !skb_shinfo (skb)->nr_frags)kfree_skb (skb);if (err == -EOVERFLOW ) {
Signature · 1 line
2.41.3
[email protected]
AI reviewRe: [PATCH net 4/4] xsk: fix use-after-free of xs->skb in xsk_build_skb() free_err path
Sashiko AI review found 3 potential issue(s): - [High] Calling `kfree_skb(skb)` in `free_err` after `xsk_skb_init_misc()` triggers premature CQ submission and corrupts Queue invariants. - [High]…
Sashiko AI review found 3 potential issue(s):
- [High] Calling kfree_skb(skb) in free_err after xsk_skb_init_misc() triggers premature CQ submission and corrupts Queue invariants.
- [High] Memory leak of xsk_addr when alloc_page() fails during multi-buffer packet processing.
- [High] On 32-bit architectures, xsk_skb_destructor_set_addr() truncates the 64-bit UMEM offset, corrupting the Completion Queue address. --
commit e60e7fb34b1c59698d11da732db02bb3302ab41a
Author: Jason Xing <[email protected]>
xsk: fix use-after-free of xs->skb in xsk_build_skb() free_err path
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. If a continuation descriptor fails, the error path frees the skb while xs->skb still points to it. This patch adds a check to ensure we only free skbs that were freshly allocated in this call.
net/xdp/xsk.c static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, · line 978
Quoted 11 lines
net/xdp/xsk.c +1
-1@@ -974,7 +974,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 && !skb_shinfo (skb)->nr_frags)kfree_skb (skb);
This isn't a regression introduced by this patch, but could calling kfree_skb() here trigger a premature completion queue submission?
If xsk_skb_metadata() fails earlier in the function, the skb destructor is already set to xsk_destruct_skb by xsk_skb_init_misc().
Freeing the skb here synchronously invokes xsk_destruct_skb(), which submits the completion to user space and increments the queue producer. The error path then executes xsk_cq_cancel_locked(), which decrements the cached producer.
Will this permanently desync the completion queue invariants and report a successful completion for a failed transmission? Would it be safer to clear the skb destructor before freeing it?
Also, is there a potential memory leak of the xsk_addr struct when alloc_page() fails during multi-buffer packet processing?
When processing a continuation descriptor, an xsk_addr struct is dynamically allocated and stored in skb_shinfo(skb)->destructor_arg with num_descs set to 1. If the subsequent alloc_page() fails, the code jumps here to free_err and returns -EAGAIN, leaving the partial packet in xs->skb for a retry.
If the socket is closed instead of retrying, xsk_drop_skb() is called on xs->skb, which calls xsk_consume_skb(). Inside xsk_consume_skb(), the cleanup logic uses this condition:
if (unlikely(num_descs > 1))
Since num_descs is exactly 1, the condition fails. Does this permanently leak the xsk_addr memory?
Finally, does xsk_skb_destructor_set_addr() truncate the 64-bit umem offset on 32-bit architectures?
The inline storage attempts to cast the address to uintptr_t:
skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
On 32-bit architectures, uintptr_t is 32 bits, which drops the upper 32 bits of the u64 addr. In af_xdp unaligned mode, the chunk offset is stored in bits 48-63.
Could this truncation lose the offset entirely, causing xsk_skb_destructor_get_addr() to retrieve a truncated address and submit it to the user-space completion queue?
Quoted 3 lines
net/xdp/xsk.c +0
-0if (err == -EOVERFLOW ) {if (xs->skb) {
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
Re: [PATCH net 4/4] xsk: fix use-after-free of xs->skb in xsk_build_skb() free_err path
Quoted 38 lines · On Sun, Apr 19, 2026 at 12:58 PM <[email protected]> wrote:
Sashiko AI review found 3 potential issue(s):
- [High] Callingkfree_skb(skb)infree_errafterxsk_skb_init_misc()triggers premature CQ submission and corrupts Queue invariants.
- [High] Memory leak ofxsk_addrwhenalloc_page()fails during multi-buffer packet processing.
- [High] On 32-bit architectures,xsk_skb_destructor_set_addr()truncates the 64-bit UMEM offset, corrupting the Completion Queue address. --commit
e60e7fb34b1c59698d11da732db02bb3302ab41a
Author: Jason Xing <[email protected]>xsk: fix use-after-free of
xs->skbinxsk_build_skb()free_errpathWhen
xsk_build_skb()processes multi-buffer packets in copy mode, the first descriptor stores data into the skb linear area without adding any frags. If a continuation descriptor fails, the error path frees the skb whilexs->skbstill points to it. This patch adds a check to ensure we only free skbs that were freshly allocated in this call.net/xdp/xsk.c static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, · line 978
Quoted 11 lines
net/xdp/xsk.c +1
-1@@ -974,7 +974,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 && !skb_shinfo (skb)->nr_frags)kfree_skb (skb);This isn't a regression introduced by this patch, but could calling
kfree_skb()here trigger a premature completion queue submission?If
xsk_skb_metadata()fails earlier in the function, the skb destructor is already set toxsk_destruct_skbbyxsk_skb_init_misc().
Quoted 8 lines
Freeing the skb here synchronously invokes
xsk_destruct_skb(), which submits the completion to user space and increments the queue producer. The error path then executesxsk_cq_cancel_locked(), which decrements the cached producer.Will this permanently desync the completion queue invariants and report a successful completion for a failed transmission? Would it be safer to clear the skb destructor before freeing it?
Ack, the same discussion again.
Quoted 10 lines
Also, is there a potential memory leak of the
xsk_addrstruct whenalloc_page()fails during multi-buffer packet processing?When processing a continuation descriptor, an
xsk_addrstruct is dynamically allocated and stored in skb_shinfo(skb)->destructor_arg withnum_descsset to 1. If the subsequentalloc_page()fails, the code jumps here tofree_errand returns-EAGAIN, leaving the partial packet inxs->skbfor a retry.
Quoted 8 lines
If the socket is closed instead of retrying,
xsk_drop_skb()is called onxs->skb, which callsxsk_consume_skb(). Insidexsk_consume_skb(), the cleanup logic uses this condition:if (
unlikely(num_descs > 1))Since
num_descsis exactly 1, the condition fails. Does this permanently leak thexsk_addrmemory?
Right. See the following inline reply.
Quoted 8 lines
Finally, does
xsk_skb_destructor_set_addr()truncate the 64-bit umem offset on 32-bit architectures?The inline storage attempts to cast the address to
uintptr_t:skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
Quoted 7 lines
On 32-bit architectures,
uintptr_tis 32 bits, which drops the upper 32 bits of the u64 addr. Inaf_xdpunaligned mode, the chunk offset is stored in bits 48-63.Could this truncation lose the offset entirely, causing
xsk_skb_destructor_get_addr()to retrieve a truncated address and submit it to the user-space completion queue?
Right. It can happen!
Brilliant, sashiko found another two pre-existing bugs here! I'm so shocked at sashiko that handles the extremely complicated path so well. It's really useful to capture the bug beforehand.
I will include the corresponding two separate patches to fix the same problematic commit:
commit 0ebc27a4c67d44e5ce88d21cdad8201862b78837
Author: Fernando Fernandez Mancera <[email protected]> Date: Mon Nov 24 18:14:09 2025 +0100
xsk: avoid data corruption on cq descriptor number
Since commit 30f241fcf52a ("xsk: Fix immature cq descriptor production"), the descriptor number is stored in skb control block and xsk_cq_submit_addr_locked() relies on it to put the umem addrs onto pool's completion queue.
skb control block shouldn't be used for this purpose as after transmit xsk doesn't have control over it and other subsystems could use it. This leads to the following kernel panic due to a NULL pointer dereference.
Thanks,
Jason
Quoted 7 lines
net/xdp/xsk.c static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, · line 980
Quoted 4 lines
net/xdp/xsk.c +0
-0if (err == -EOVERFLOW ) {if (xs->skb) {--
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4