FAILED: patch "[PATCH] xfrm: save input state data before secpath resets" failed to apply to 6.18-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 6.18-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.18.y
git checkout FETCH_HEAD
git cherry-pick -x 3cf5cdecd99c9c186a5ea518d93bbf3045b6e3aa
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026092225-extortion-shelving-5087@gregkh' --subject-prefix 'PATCH 6.18.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 3cf5cdecd99c9c186a5ea518d93bbf3045b6e3aa Mon Sep 17 00:00:00 2001
From: Zhiling Zou <[email protected]>
Date: Sat, 29 Aug 2026 17:24:24 +0800
Subject: [PATCH] xfrm: save input state data before secpath resets
xfrm_input() stores the current xfrm_state in the skb secpath while it continues receive-side processing. Some input paths can reset that secpath before xfrm_input() has finished dereferencing the state.
Receive callback users such as VTI and XFRM interfaces can reset the secpath. The VTI receive path does so before checking whether the packet crosses network namespaces, while the XFRM interface path does so only for cross-network-namespace packets. The XFRM_MAX_DEPTH error path can also reset the secpath before the final drop callback reports the current state's protocol.
If secpath_reset() drops the last state reference while the state is concurrently deleted, xfrm_input() can still dereference the freed state when selecting transport_finish() or reporting the drop callback protocol.
Save the state protocol on the stack while the state is still valid, and use the already saved address family for transport_finish(). A larval XFRM_STATE_ACQ state has no type, so retain nexthdr as its protocol. This preserves the existing drop-path fallback while avoiding the post-reset state dereferences without adding an extra state reference to every received packet.
net/xfrm/xfrm_input.c +8 -3
@@ -474,6 +474,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)struct xfrm_state *x =NULL ;xfrm_address_t *daddr;u32 mark = skb->mark; +u8 xfrm_proto = nexthdr;unsigned int family =AF_UNSPEC ;int decaps =0 ;int async =0 ;
@@ -485,6 +486,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)if (encap_type <0 || (xo && (xo->flags &XFRM_GRO || encap_type ==0 || encap_type ==UDP_ENCAP_ESPINUDP ))) { x =xfrm_input_state (skb); + xfrm_proto = x->type ? x->type->proto : nexthdr;if (unlikely (x->km.state !=XFRM_STATE_VALID )) {if (x->km.state ==XFRM_STATE_ACQ )
@@ -592,11 +594,13 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) x =xfrm_input_state_lookup (net, mark, daddr, spi, nexthdr, family);if (x ==NULL ) { + xfrm_proto = nexthdr;secpath_reset (skb);XFRM_INC_STATS (net,LINUX_MIB_XFRMINNOSTATES );xfrm_audit_state_notfound (skb, family, spi, seq); goto drop; } + xfrm_proto = x->type ? x->type->proto : nexthdr;if (unlikely (x->dir && x->dir !=XFRM_SA_DIR_IN )) {secpath_reset (skb);
@@ -604,6 +608,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)xfrm_audit_state_notfound (skb, family, spi, seq);xfrm_state_put (x); x =NULL ; + xfrm_proto = nexthdr; goto drop; }
@@ -728,7 +733,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) }while (!err);rcu_read_lock (); - err =xfrm_rcv_cb (skb, family, x->type->proto,0 ); + err =xfrm_rcv_cb (skb, family, xfrm_proto,0 );if (err) {rcu_read_unlock (); goto drop;
@@ -753,7 +758,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) xfrm_gro = xo->flags &XFRM_GRO ; err = -EAFNOSUPPORT ; - afinfo =xfrm_state_afinfo_get_rcu (x->props.family); + afinfo =xfrm_state_afinfo_get_rcu (family);if (likely (afinfo)) err = afinfo->transport_finish (skb, xfrm_gro || async);if (xfrm_gro) {
@@ -776,7 +781,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) drop:if (async)dev_put (dev); -xfrm_rcv_cb (skb, family, x && x->type ? x->type->proto : nexthdr,-1 ); +xfrm_rcv_cb (skb, family, xfrm_proto,-1 );kfree_skb (skb);return 0 ; }
Sasha Levin
maintainerPATCH 1/3 xfrm: Refactor xfrm_input lock to reduce contention with RSS
From: Jianbo Liu <[email protected]>
[ Upstream commit 10a11861943902fda74f37f456b45183b2bca270 ]
With newer NICs like mlx5 supporting RSS for IPsec crypto offload, packets for a single Security Association (SA) are scattered across multiple CPU cores for parallel processing. The xfrm_state spinlock (x->lock) is held for each packet during xfrm processing.
When multiple connections or flows share the same SA, this parallelism causes high lock contention on x->lock, creating a performance bottleneck and limiting scalability.
The original xfrm_input() function exacerbated this issue by releasing and immediately re-acquiring x->lock. For hardware crypto offload paths, this unlock/relock sequence is unnecessary and introduces significant overhead. This patch refactors the function to relocate the type_offload->input_tail call for the offload path, performing all necessary work while continuously holding the lock. This reordering is safe, since packets which don't pass the checks below will still fail them with the new code.
Performance testing with iperf using multiple parallel streams over a single IPsec SA shows significant improvement in throughput as the number of queues (and thus CPU cores) increases:
+-----------+---------------+--------------+-----------------+ | RX queues | Before (Gbps) | After (Gbps) | Improvement (%) | +-----------+---------------+--------------+-----------------+ | 2 | 32.3 | 34.4 | 6.5 | | 4 | 34.4 | 40.0 | 16.3 | | 6 | 24.5 | 38.3 | 56.3 | | 8 | 23.1 | 38.3 | 65.8 | | 12 | 18.1 | 29.9 | 65.2 | | 16 | 16.0 | 25.2 | 57.5 | +-----------+---------------+--------------+-----------------+
Stable-dep-of: 3cf5cdecd99c ("xfrm: save input state data before secpath resets")
--- net/xfrm/xfrm_input.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
net/xfrm/xfrm_input.c +7 -7
@@ -504,6 +504,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)if (encap_type ==-1 ) { async =1 ; seq =XFRM_SKB_CB (skb)->seq.input.low; +spin_lock (&x->lock); goto resume; }/* GRO call */
@@ -540,6 +541,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)XFRM_INC_STATS (net,LINUX_MIB_XFRMINHDRERROR ); goto drop; } + + nexthdr = x->type_offload->input_tail (x, skb); } goto lock;
@@ -637,11 +640,9 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) goto drop_unlock; } -spin_unlock (&x->lock); -if (xfrm_tunnel_check (skb, x, family)) {XFRM_INC_STATS (net,LINUX_MIB_XFRMINSTATEMODEERROR ); - goto drop; + goto drop_unlock; } seq_hi =htonl (xfrm_replay_seqhi (x, seq));
@@ -649,9 +650,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)XFRM_SKB_CB (skb)->seq.input.low = seq;XFRM_SKB_CB (skb)->seq.input.hi = seq_hi; -if (crypto_done) { - nexthdr = x->type_offload->input_tail (x, skb); - }else { +if (!crypto_done) { +spin_unlock (&x->lock);dev_hold (skb->dev); nexthdr = x->type->input (x, skb);
@@ -662,9 +662,9 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) }dev_put (skb->dev); +spin_lock (&x->lock); } resume: -spin_lock (&x->lock);if (nexthdr <0 ) {if (nexthdr == -EBADMSG ) { xfrm_audit_state_icvfail(x, skb,
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/3 xfrm: Fix dev use-after-free in xfrm async resumption
From: Dong Chenchen <[email protected]>
[ Upstream commit 8045c0df98d4f14c54e5cb875f1c9c0ce89fe4ff ]
xfrm async resumption hold skb->dev refcnt until after transport_finish. However, xfrm_rcv_cb may modify skb->dev to tunnel dev without taking device reference, such as vti_rcv_cb. The subsequent async resumption will decrement the tunnel device's reference count, which lead to uaf of tunnel dev and refcnt leak of orig dev as below:
unregister_netdevice: waiting for vti1 to become free. Usage count = -2
Stash the original skb->dev to fix refcnt imbalance. The new skb->dev set by xfrm_rcv_cb can race with device teardown. Extend rcu protection over xfrm_rcv_cb and transport_finish to prevent races.
Stable-dep-of: 3cf5cdecd99c ("xfrm: save input state data before secpath resets")
--- net/ipv4/xfrm4_input.c | 2 -- net/ipv6/xfrm6_input.c | 2 -- net/xfrm/xfrm_input.c | 29 ++++++++++++++++------------- 3 files changed, 16 insertions(+), 17 deletions(-)
net/ipv4/xfrm4_input.c +0 -2
@@ -76,8 +76,6 @@ int xfrm4_transport_finish(struct sk_buff *skb, int async)NF_HOOK (NFPROTO_IPV4 ,NF_INET_PRE_ROUTING ,dev_net (dev),NULL , skb, dev,NULL , xfrm4_rcv_encap_finish); -if (async) -dev_put (dev);return 0 ; }
net/ipv6/xfrm6_input.c +0 -2
@@ -71,8 +71,6 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async)NF_HOOK (NFPROTO_IPV6 ,NF_INET_PRE_ROUTING ,dev_net (dev),NULL , skb, dev,NULL , xfrm6_transport_finish2); -if (async) -dev_put (dev);return 0 ; }
net/xfrm/xfrm_input.c +16 -13
@@ -464,6 +464,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) {const struct xfrm_state_afinfo *afinfo;struct net *net =dev_net (skb->dev); +struct net_device *dev = skb->dev;int err;__be32 seq;__be32 seq_hi;
@@ -490,7 +491,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) LINUX_MIB_XFRMINSTATEINVALID);if (encap_type ==-1 ) -dev_put (skb->dev); +dev_put (dev); goto drop; }
@@ -652,16 +653,16 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)if (!crypto_done) {spin_unlock (&x->lock); -dev_hold (skb->dev); +dev_hold (dev); nexthdr = x->type->input (x, skb);if (nexthdr == -EINPROGRESS ) {if (async) -dev_put (skb->dev); +dev_put (dev);return 0 ; } -dev_put (skb->dev); +dev_put (dev);spin_lock (&x->lock); } resume:
@@ -696,7 +697,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) err =xfrm_inner_mode_input (x, skb);if (err == -EINPROGRESS ) {if (async) -dev_put (skb->dev); +dev_put (dev);return 0 ; }else if (err) {XFRM_INC_STATS (net,LINUX_MIB_XFRMINSTATEMODEERROR );
@@ -723,9 +724,12 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) crypto_done = false; }while (!err); +rcu_read_lock (); err =xfrm_rcv_cb (skb, family, x->type->proto,0 ); -if (err) +if (err) { +rcu_read_unlock (); goto drop; + }nf_reset_ct (skb);
@@ -736,8 +740,9 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)if (skb_valid_dst (skb))skb_dst_drop (skb);if (async) -dev_put (skb->dev); +dev_put (dev);gro_cells_receive (&gro_cells, skb); +rcu_read_unlock ();return 0 ; }else { xo =xfrm_offload (skb);
@@ -745,23 +750,21 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) xfrm_gro = xo->flags &XFRM_GRO ; err = -EAFNOSUPPORT ; -rcu_read_lock (); afinfo =xfrm_state_afinfo_get_rcu (x->props.family);if (likely (afinfo)) err = afinfo->transport_finish (skb, xfrm_gro || async); -rcu_read_unlock ();if (xfrm_gro) { sp =skb_sec_path (skb);if (sp) 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; } +if (async) +dev_put (dev); +rcu_read_unlock ();return err; }
@@ -769,7 +772,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)spin_unlock (&x->lock); drop:if (async) -dev_put (skb->dev); +dev_put (dev);xfrm_rcv_cb (skb, family, x && x->type ? x->type->proto : nexthdr,-1 );kfree_skb (skb);return 0 ;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/3 xfrm: save input state data before secpath resets
In reply to Sasha LevinFrom: Zhiling Zou <[email protected]>
[ Upstream commit 3cf5cdecd99c9c186a5ea518d93bbf3045b6e3aa ]
xfrm_input() stores the current xfrm_state in the skb secpath while it continues receive-side processing. Some input paths can reset that secpath before xfrm_input() has finished dereferencing the state.
Receive callback users such as VTI and XFRM interfaces can reset the secpath. The VTI receive path does so before checking whether the packet crosses network namespaces, while the XFRM interface path does so only for cross-network-namespace packets. The XFRM_MAX_DEPTH error path can also reset the secpath before the final drop callback reports the current state's protocol.
If secpath_reset() drops the last state reference while the state is concurrently deleted, xfrm_input() can still dereference the freed state when selecting transport_finish() or reporting the drop callback protocol.
Save the state protocol on the stack while the state is still valid, and use the already saved address family for transport_finish(). A larval XFRM_STATE_ACQ state has no type, so retain nexthdr as its protocol. This preserves the existing drop-path fallback while avoiding the post-reset state dereferences without adding an extra state reference to every received packet.
--- net/xfrm/xfrm_input.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
net/xfrm/xfrm_input.c +8 -3
@@ -471,6 +471,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)struct xfrm_state *x =NULL ;xfrm_address_t *daddr;u32 mark = skb->mark; +u8 xfrm_proto = nexthdr;unsigned int family =AF_UNSPEC ;int decaps =0 ;int async =0 ;
@@ -482,6 +483,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)if (encap_type <0 || (xo && (xo->flags &XFRM_GRO || encap_type ==0 || encap_type ==UDP_ENCAP_ESPINUDP ))) { x =xfrm_input_state (skb); + xfrm_proto = x->type ? x->type->proto : nexthdr;if (unlikely (x->km.state !=XFRM_STATE_VALID )) {if (x->km.state ==XFRM_STATE_ACQ )
@@ -589,11 +591,13 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) x =xfrm_input_state_lookup (net, mark, daddr, spi, nexthdr, family);if (x ==NULL ) { + xfrm_proto = nexthdr;secpath_reset (skb);XFRM_INC_STATS (net,LINUX_MIB_XFRMINNOSTATES );xfrm_audit_state_notfound (skb, family, spi, seq); goto drop; } + xfrm_proto = x->type ? x->type->proto : nexthdr;if (unlikely (x->dir && x->dir !=XFRM_SA_DIR_IN )) {secpath_reset (skb);
@@ -601,6 +605,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)xfrm_audit_state_notfound (skb, family, spi, seq);xfrm_state_put (x); x =NULL ; + xfrm_proto = nexthdr; goto drop; }
@@ -725,7 +730,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) }while (!err);rcu_read_lock (); - err =xfrm_rcv_cb (skb, family, x->type->proto,0 ); + err =xfrm_rcv_cb (skb, family, xfrm_proto,0 );if (err) {rcu_read_unlock (); goto drop;
@@ -750,7 +755,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) xfrm_gro = xo->flags &XFRM_GRO ; err = -EAFNOSUPPORT ; - afinfo =xfrm_state_afinfo_get_rcu (x->props.family); + afinfo =xfrm_state_afinfo_get_rcu (family);if (likely (afinfo)) err = afinfo->transport_finish (skb, xfrm_gro || async);if (xfrm_gro) {
@@ -773,7 +778,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) drop:if (async)dev_put (dev); -xfrm_rcv_cb (skb, family, x && x->type ? x->type->proto : nexthdr,-1 ); +xfrm_rcv_cb (skb, family, xfrm_proto,-1 );kfree_skb (skb);return 0 ; }
Signature · 1 line
2.53.0