Netdev List
 help / color / mirror / Atom feed
* [PATCH net v4] xfrm: propagate -EINPROGRESS from validate_xmit_xfrm()
@ 2026-06-03  6:46 Petr Wozniak
  2026-06-04 11:29 ` Sabrina Dubroca
  0 siblings, 1 reply; 2+ messages in thread
From: Petr Wozniak @ 2026-06-03  6:46 UTC (permalink / raw)
  To: netdev; +Cc: sd, steffen.klassert, pabeni, edumazet, Petr Wozniak

validate_xmit_xfrm() returns NULL both when a packet is dropped and
when it is stolen by async crypto (-EINPROGRESS from ->xmit()).
Callers cannot distinguish the two cases.

f53c723902d1 ("net: Add asynchronous callbacks for xfrm on layer 2.")
changed the semantics of a NULL return from "dropped" to "stolen or
dropped", but __dev_queue_xmit() was not updated.  On virtual/bridge
interfaces (noqueue qdisc) __dev_queue_xmit() initialises rc=-ENOMEM
and jumps to out: when skb is NULL, returning -ENOMEM to the caller
even though the packet will be delivered correctly via xfrm_dev_resume().

Return ERR_PTR(-EINPROGRESS) from validate_xmit_xfrm() for the async
case so callers can tell it apart from a real drop.  Update
__dev_queue_xmit() to handle ERR_PTR(-EINPROGRESS) from
validate_xmit_skb() correctly.  Update validate_xmit_skb_list() to
use IS_ERR_OR_NULL() so that ERR_PTR(-EINPROGRESS) is not mistakenly
added to the transmitted list.

Fixes: f53c723902d1 ("net: Add asynchronous callbacks for xfrm on layer 2.")
Suggested-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: Petr Wozniak <petr.wozniak@gmail.com>
---
Changes in v4:
 - Drop bool stolen tracking in validate_xmit_skb_list() and ERR_PTR
   return from that function; use IS_ERR_OR_NULL() in the loop so
   stolen skbs are silently skipped (same as the pre-patch NULL behaviour)
   (Sabrina Dubroca)
 - Drop ERR_PTR(-EINPROGRESS) handling in __dev_direct_xmit(); callers
   cannot reach the -EINPROGRESS path of validate_xmit_xfrm()
   (Sabrina Dubroca)
 - Move validate_xmit_skb() return-value comment above the function
   instead of inline in __dev_queue_xmit() (Sabrina Dubroca)

Changes in v3:
 - Fix validate_xmit_skb_list(): set stolen=true only for -EINPROGRESS,
   not for all IS_ERR() cases (Sabrina Dubroca)
 - Update __dev_direct_xmit() to handle ERR_PTR(-EINPROGRESS) from
   validate_xmit_skb_list() before kfree_skb_list() which would crash
   on an ERR_PTR (Sabrina Dubroca / sashiko)

Changes in v2:
 - Only reset rc to NET_XMIT_SUCCESS when PTR_ERR(skb) == -EINPROGRESS,
   not for any IS_ERR() result (Sabrina Dubroca)
 - Add comment explaining why rc is reset and the async delivery path
 - Fix validate_xmit_skb_list(): track stolen skbs and return
   ERR_PTR(-EINPROGRESS) when all skbs in the list were stolen by
   async crypto, not NULL (Sabrina Dubroca)
---
 net/core/dev.c         | 11 ++++++++---
 net/xfrm/xfrm_device.c |  2 +-
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/net/core/dev.c b/net/core/dev.c
index 0c6c270d9..18e92bef7 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4016,6 +4016,9 @@ static struct sk_buff *validate_xmit_unreadable_skb(struct sk_buff *skb,
 	return NULL;
 }
 
+/* Returns the skb on success, NULL if dropped, or ERR_PTR(-EINPROGRESS)
+ * if stolen by async xfrm crypto (delivered via xfrm_dev_resume()).
+ */
 static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev, bool *again)
 {
 	netdev_features_t features;
@@ -4078,7 +4081,6 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device
 struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev, bool *again)
 {
 	struct sk_buff *next, *head = NULL, *tail;
-
 	for (; skb != NULL; skb = next) {
 		next = skb->next;
 		skb_mark_not_on_list(skb);
@@ -4087,7 +4089,7 @@ struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *d
 		skb->prev = skb;
 
 		skb = validate_xmit_skb(skb, dev, again);
-		if (!skb)
+		if (IS_ERR_OR_NULL(skb))
 			continue;
 
 		if (!head)
@@ -4858,8 +4860,11 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)
 			goto recursion_alert;
 
 		skb = validate_xmit_skb(skb, dev, &again);
-		if (!skb)
+		if (IS_ERR_OR_NULL(skb)) {
+			if (PTR_ERR(skb) == -EINPROGRESS)
+				rc = NET_XMIT_SUCCESS;
 			goto out;
+		}
 
 		HARD_TX_LOCK(dev, txq, cpu);
 
diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
index 550457e4c..bb0c0fafa 100644
--- a/net/xfrm/xfrm_device.c
+++ b/net/xfrm/xfrm_device.c
@@ -182,7 +182,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
 		err = x->type_offload->xmit(x, skb, esp_features);
 		if (err) {
 			if (err == -EINPROGRESS)
-				return NULL;
+				return ERR_PTR(-EINPROGRESS);
 
 			XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR);
 			kfree_skb(skb);
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH net v4] xfrm: propagate -EINPROGRESS from validate_xmit_xfrm()
  2026-06-03  6:46 [PATCH net v4] xfrm: propagate -EINPROGRESS from validate_xmit_xfrm() Petr Wozniak
@ 2026-06-04 11:29 ` Sabrina Dubroca
  0 siblings, 0 replies; 2+ messages in thread
From: Sabrina Dubroca @ 2026-06-04 11:29 UTC (permalink / raw)
  To: Petr Wozniak; +Cc: netdev, steffen.klassert, pabeni, edumazet

2026-06-03, 08:46:59 +0200, Petr Wozniak wrote:
> +/* Returns the skb on success, NULL if dropped, or ERR_PTR(-EINPROGRESS)
> + * if stolen by async xfrm crypto (delivered via xfrm_dev_resume()).
> + */
>  static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev, bool *again)
>  {
>  	netdev_features_t features;
> @@ -4078,7 +4081,6 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device
>  struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev, bool *again)
>  {
>  	struct sk_buff *next, *head = NULL, *tail;
> -

nit: no, that blank line needs to stay

>  	for (; skb != NULL; skb = next) {
>  		next = skb->next;
>  		skb_mark_not_on_list(skb);
> @@ -4087,7 +4089,7 @@ struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *d
>  		skb->prev = skb;
>  
>  		skb = validate_xmit_skb(skb, dev, again);
> -		if (!skb)
> +		if (IS_ERR_OR_NULL(skb))
>  			continue;
>  
>  		if (!head)

[...]
> diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
> index 550457e4c..bb0c0fafa 100644
> --- a/net/xfrm/xfrm_device.c
> +++ b/net/xfrm/xfrm_device.c
> @@ -182,7 +182,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
>  		err = x->type_offload->xmit(x, skb, esp_features);
>  		if (err) {
>  			if (err == -EINPROGRESS)
> -				return NULL;
> +				return ERR_PTR(-EINPROGRESS);
>  
>  			XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR);
>  			kfree_skb(skb);

Could you please explain why you're ignoring my comment about the
2nd x->type_offload->xmit() call in this function?

Thanks,

-- 
Sabrina

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-06-04 11:29 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-03  6:46 [PATCH net v4] xfrm: propagate -EINPROGRESS from validate_xmit_xfrm() Petr Wozniak
2026-06-04 11:29 ` Sabrina Dubroca

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox