]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xfrm: propagate -EINPROGRESS from validate_xmit_xfrm()
authorPetr Wozniak <petr.wozniak@gmail.com>
Sun, 21 Jun 2026 10:03:26 +0000 (12:03 +0200)
committerSteffen Klassert <steffen.klassert@secunet.com>
Fri, 26 Jun 2026 06:13:54 +0000 (08:13 +0200)
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>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
net/core/dev.c
net/xfrm/xfrm_device.c

index 4b3d5cfdf6e00fe76ab50f10d2c60b80d65ac74f..5933c5dab09ee1f8fdde3f79d0da87531193bdcd 100644 (file)
@@ -4018,6 +4018,9 @@ out_free:
        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;
@@ -4089,7 +4092,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)
@@ -4860,8 +4863,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);
 
index 630f3dd31cc5cea094da1d4819547bfe503a1225..19c77f09acc95611ccb7befe90e373b59ca08343 100644 (file)
@@ -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);
@@ -224,7 +224,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
                pskb = skb2;
        }
 
-       return skb;
+       return skb ? skb : ERR_PTR(-EINPROGRESS);
 }
 EXPORT_SYMBOL_GPL(validate_xmit_xfrm);