+++ /dev/null
-From c23bf6bc628df4f85442f73c1e190b74dae30031 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Fri, 5 Jun 2026 14:12:54 +0000
-Subject: xfrm: hold dev ref until after transport_finish NF_HOOK
-
-From: Qi Tang <tpluszz77@gmail.com>
-
-[ Upstream commit 1c428b03840094410c5fb6a5db30640486bbbfcb ]
-
-After async crypto completes, xfrm_input_resume() calls dev_put()
-immediately on re-entry before the skb reaches transport_finish.
-The skb->dev pointer is then used inside NF_HOOK and its okfn,
-which can race with device teardown.
-
-Remove the dev_put from the async resumption entry and instead
-drop the reference after the NF_HOOK call in transport_finish,
-using a saved device pointer since NF_HOOK may consume the skb.
-This covers NF_DROP, NF_QUEUE and NF_STOLEN paths that skip
-the okfn.
-
-For non-transport exits (decaps, gro, drop) and secondary
-async return points, release the reference inline when
-async is set.
-
-Suggested-by: Florian Westphal <fw@strlen.de>
-Fixes: acf568ee859f ("xfrm: Reinject transport-mode packets through tasklet")
-Cc: stable@vger.kernel.org
-Signed-off-by: Qi Tang <tpluszz77@gmail.com>
-Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
-[ net/xfrm/xfrm_input.c: dev_hold/dev_put are unconditional here rather
-than inside !crypto_done as in mainline, and the dev_put in the
-encap_type == -1 async-resumption block does not exist; adapted by
-gating dev_put at resume: with if (!async), adding if (async) dev_put
-at -EINPROGRESS return, gro_cells_receive paths, and drop label. ]
-Signed-off-by: Simon Liebold <simonlie@amazon.de>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- net/ipv4/xfrm4_input.c | 5 ++++-
- net/ipv6/xfrm6_input.c | 5 ++++-
- net/xfrm/xfrm_input.c | 14 ++++++++++++--
- 3 files changed, 20 insertions(+), 4 deletions(-)
-
-diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c
-index 12a1a0f421956c..adf21d6b6076c1 100644
---- a/net/ipv4/xfrm4_input.c
-+++ b/net/ipv4/xfrm4_input.c
-@@ -50,6 +50,7 @@ int xfrm4_transport_finish(struct sk_buff *skb, int async)
- {
- struct xfrm_offload *xo = xfrm_offload(skb);
- struct iphdr *iph = ip_hdr(skb);
-+ struct net_device *dev = skb->dev;
-
- iph->protocol = XFRM_MODE_SKB_CB(skb)->protocol;
-
-@@ -73,8 +74,10 @@ int xfrm4_transport_finish(struct sk_buff *skb, int async)
- }
-
- NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
-- dev_net(skb->dev), NULL, skb, skb->dev, NULL,
-+ dev_net(dev), NULL, skb, dev, NULL,
- xfrm4_rcv_encap_finish);
-+ if (async)
-+ dev_put(dev);
- return 0;
- }
-
-diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c
-index 9005fc156a20e6..699a001ac16629 100644
---- a/net/ipv6/xfrm6_input.c
-+++ b/net/ipv6/xfrm6_input.c
-@@ -43,6 +43,7 @@ static int xfrm6_transport_finish2(struct net *net, struct sock *sk,
- int xfrm6_transport_finish(struct sk_buff *skb, int async)
- {
- struct xfrm_offload *xo = xfrm_offload(skb);
-+ struct net_device *dev = skb->dev;
- int nhlen = -skb_network_offset(skb);
-
- skb_network_header(skb)[IP6CB(skb)->nhoff] =
-@@ -68,8 +69,10 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async)
- }
-
- NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING,
-- dev_net(skb->dev), NULL, skb, skb->dev, NULL,
-+ dev_net(dev), NULL, skb, dev, NULL,
- xfrm6_transport_finish2);
-+ if (async)
-+ dev_put(dev);
- return 0;
- }
-
-diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
-index 8edcb32735e595..a0a04b87a0c2ab 100644
---- a/net/xfrm/xfrm_input.c
-+++ b/net/xfrm/xfrm_input.c
-@@ -645,10 +645,14 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
- else
- nexthdr = x->type->input(x, skb);
-
-- if (nexthdr == -EINPROGRESS)
-+ if (nexthdr == -EINPROGRESS) {
-+ if (async)
-+ dev_put(skb->dev);
- return 0;
-+ }
- resume:
-- dev_put(skb->dev);
-+ if (!async)
-+ dev_put(skb->dev);
-
- spin_lock(&x->lock);
- if (nexthdr < 0) {
-@@ -716,6 +720,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
- sp->olen = 0;
- if (skb_valid_dst(skb))
- skb_dst_drop(skb);
-+ if (async)
-+ dev_put(skb->dev);
- gro_cells_receive(&gro_cells, skb);
- return 0;
- } else {
-@@ -735,6 +741,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
- sp->olen = 0;
- if (skb_valid_dst(skb))
- skb_dst_drop(skb);
-+ if (async)
-+ dev_put(skb->dev);
- gro_cells_receive(&gro_cells, skb);
- return err;
- }
-@@ -745,6 +753,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
- drop_unlock:
- spin_unlock(&x->lock);
- drop:
-+ if (async)
-+ dev_put(skb->dev);
- xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1);
- kfree_skb(skb);
- return 0;
---
-2.53.0
-