]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for all trees
authorSasha Levin <sashal@kernel.org>
Sun, 7 Dec 2025 00:36:41 +0000 (19:36 -0500)
committerSasha Levin <sashal@kernel.org>
Sun, 7 Dec 2025 00:36:41 +0000 (19:36 -0500)
Signed-off-by: Sasha Levin <sashal@kernel.org>
32 files changed:
queue-5.10/revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch [new file with mode: 0644]
queue-5.10/series [new file with mode: 0644]
queue-5.10/xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch [new file with mode: 0644]
queue-5.10/xfrm-delete-x-tunnel-as-we-delete-x.patch [new file with mode: 0644]
queue-5.10/xfrm-flush-all-states-in-xfrm_state_fini.patch [new file with mode: 0644]
queue-5.15/dpaa2-mac-bail-if-the-dpmacs-fwnode-is-not-found.patch [new file with mode: 0644]
queue-5.15/drm-i915-selftests-fix-inconsistent-is_err-and-ptr_e.patch [new file with mode: 0644]
queue-5.15/leds-replace-all-non-returning-strlcpy-with-strscpy.patch [new file with mode: 0644]
queue-5.15/leds-spi-byte-use-devm_led_classdev_register_ext.patch [new file with mode: 0644]
queue-5.15/revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch [new file with mode: 0644]
queue-5.15/series [new file with mode: 0644]
queue-5.15/xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch [new file with mode: 0644]
queue-5.15/xfrm-delete-x-tunnel-as-we-delete-x.patch [new file with mode: 0644]
queue-5.15/xfrm-flush-all-states-in-xfrm_state_fini.patch [new file with mode: 0644]
queue-6.1/leds-replace-all-non-returning-strlcpy-with-strscpy.patch [new file with mode: 0644]
queue-6.1/leds-spi-byte-use-devm_led_classdev_register_ext.patch [new file with mode: 0644]
queue-6.1/revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch [new file with mode: 0644]
queue-6.1/series [new file with mode: 0644]
queue-6.1/xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch [new file with mode: 0644]
queue-6.1/xfrm-delete-x-tunnel-as-we-delete-x.patch [new file with mode: 0644]
queue-6.1/xfrm-flush-all-states-in-xfrm_state_fini.patch [new file with mode: 0644]
queue-6.12/revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch [new file with mode: 0644]
queue-6.12/series [new file with mode: 0644]
queue-6.12/xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch [new file with mode: 0644]
queue-6.12/xfrm-delete-x-tunnel-as-we-delete-x.patch [new file with mode: 0644]
queue-6.12/xfrm-flush-all-states-in-xfrm_state_fini.patch [new file with mode: 0644]
queue-6.6/leds-spi-byte-use-devm_led_classdev_register_ext.patch [new file with mode: 0644]
queue-6.6/revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch [new file with mode: 0644]
queue-6.6/series [new file with mode: 0644]
queue-6.6/xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch [new file with mode: 0644]
queue-6.6/xfrm-delete-x-tunnel-as-we-delete-x.patch [new file with mode: 0644]
queue-6.6/xfrm-flush-all-states-in-xfrm_state_fini.patch [new file with mode: 0644]

diff --git a/queue-5.10/revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch b/queue-5.10/revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch
new file mode 100644 (file)
index 0000000..d26ed2a
--- /dev/null
@@ -0,0 +1,176 @@
+From 7490f3898bf537cf41306de197a35430f590a15f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 29 Jul 2025 17:11:53 -0400
+Subject: Revert "xfrm: destroy xfrm_state synchronously on net exit path"
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit 2a198bbec6913ae1c90ec963750003c6213668c7 ]
+
+This reverts commit f75a2804da391571563c4b6b29e7797787332673.
+
+With all states (whether user or kern) removed from the hashtables
+during deletion, there's no need for synchronous destruction of
+states. xfrm6_tunnel states still need to have been destroyed (which
+will be the case when its last user is deleted (not destroyed)) so
+that xfrm6_tunnel_free_spi removes it from the per-netns hashtable
+before the netns is destroyed.
+
+This has the benefit of skipping one synchronize_rcu per state (in
+__xfrm_state_destroy(sync=true)) when we exit a netns.
+
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/xfrm.h      | 12 +++---------
+ net/ipv6/xfrm6_tunnel.c |  2 +-
+ net/key/af_key.c        |  2 +-
+ net/xfrm/xfrm_state.c   | 23 +++++++++--------------
+ net/xfrm/xfrm_user.c    |  2 +-
+ 5 files changed, 15 insertions(+), 26 deletions(-)
+
+diff --git a/include/net/xfrm.h b/include/net/xfrm.h
+index a8584de9b18b7..411949a66a83c 100644
+--- a/include/net/xfrm.h
++++ b/include/net/xfrm.h
+@@ -794,7 +794,7 @@ static inline void xfrm_pols_put(struct xfrm_policy **pols, int npols)
+               xfrm_pol_put(pols[i]);
+ }
+-void __xfrm_state_destroy(struct xfrm_state *, bool);
++void __xfrm_state_destroy(struct xfrm_state *);
+ static inline void __xfrm_state_put(struct xfrm_state *x)
+ {
+@@ -804,13 +804,7 @@ static inline void __xfrm_state_put(struct xfrm_state *x)
+ static inline void xfrm_state_put(struct xfrm_state *x)
+ {
+       if (refcount_dec_and_test(&x->refcnt))
+-              __xfrm_state_destroy(x, false);
+-}
+-
+-static inline void xfrm_state_put_sync(struct xfrm_state *x)
+-{
+-      if (refcount_dec_and_test(&x->refcnt))
+-              __xfrm_state_destroy(x, true);
++              __xfrm_state_destroy(x);
+ }
+ static inline void xfrm_state_hold(struct xfrm_state *x)
+@@ -1585,7 +1579,7 @@ struct xfrmk_spdinfo {
+ struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq);
+ int xfrm_state_delete(struct xfrm_state *x);
+-int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync);
++int xfrm_state_flush(struct net *net, u8 proto, bool task_valid);
+ int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid);
+ void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
+ void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
+diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
+index 61ffc01b6c479..70f9540937489 100644
+--- a/net/ipv6/xfrm6_tunnel.c
++++ b/net/ipv6/xfrm6_tunnel.c
+@@ -331,7 +331,7 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
+       struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
+       unsigned int i;
+-      xfrm_state_flush(net, 0, false, true);
++      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
+       xfrm_flush_gc();
+       for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
+diff --git a/net/key/af_key.c b/net/key/af_key.c
+index f42854973ba8d..de4606d2eb643 100644
+--- a/net/key/af_key.c
++++ b/net/key/af_key.c
+@@ -1770,7 +1770,7 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_m
+       if (proto == 0)
+               return -EINVAL;
+-      err = xfrm_state_flush(net, proto, true, false);
++      err = xfrm_state_flush(net, proto, true);
+       err2 = unicast_flush_resp(sk, hdr);
+       if (err || err2) {
+               if (err == -ESRCH) /* empty table - go quietly */
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index 3cd878a25602a..a45d7e1dc5c6f 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -476,7 +476,7 @@ void xfrm_state_free(struct xfrm_state *x)
+ }
+ EXPORT_SYMBOL(xfrm_state_free);
+-static void ___xfrm_state_destroy(struct xfrm_state *x)
++static void xfrm_state_gc_destroy(struct xfrm_state *x)
+ {
+       hrtimer_cancel(&x->mtimer);
+       del_timer_sync(&x->rtimer);
+@@ -514,7 +514,7 @@ static void xfrm_state_gc_task(struct work_struct *work)
+       synchronize_rcu();
+       hlist_for_each_entry_safe(x, tmp, &gc_list, gclist)
+-              ___xfrm_state_destroy(x);
++              xfrm_state_gc_destroy(x);
+ }
+ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
+@@ -637,19 +637,14 @@ struct xfrm_state *xfrm_state_alloc(struct net *net)
+ }
+ EXPORT_SYMBOL(xfrm_state_alloc);
+-void __xfrm_state_destroy(struct xfrm_state *x, bool sync)
++void __xfrm_state_destroy(struct xfrm_state *x)
+ {
+       WARN_ON(x->km.state != XFRM_STATE_DEAD);
+-      if (sync) {
+-              synchronize_rcu();
+-              ___xfrm_state_destroy(x);
+-      } else {
+-              spin_lock_bh(&xfrm_state_gc_lock);
+-              hlist_add_head(&x->gclist, &xfrm_state_gc_list);
+-              spin_unlock_bh(&xfrm_state_gc_lock);
+-              schedule_work(&xfrm_state_gc_work);
+-      }
++      spin_lock_bh(&xfrm_state_gc_lock);
++      hlist_add_head(&x->gclist, &xfrm_state_gc_list);
++      spin_unlock_bh(&xfrm_state_gc_lock);
++      schedule_work(&xfrm_state_gc_work);
+ }
+ EXPORT_SYMBOL(__xfrm_state_destroy);
+@@ -758,7 +753,7 @@ xfrm_dev_state_flush_secctx_check(struct net *net, struct net_device *dev, bool
+ }
+ #endif
+-int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync)
++int xfrm_state_flush(struct net *net, u8 proto, bool task_valid)
+ {
+       int i, err = 0, cnt = 0;
+@@ -2707,7 +2702,7 @@ void xfrm_state_fini(struct net *net)
+       unsigned int sz;
+       flush_work(&net->xfrm.state_hash_work);
+-      xfrm_state_flush(net, 0, false, true);
++      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
+       flush_work(&xfrm_state_gc_work);
+       WARN_ON(!list_empty(&net->xfrm.state_all));
+diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
+index aa509857b6660..480da22b7ef85 100644
+--- a/net/xfrm/xfrm_user.c
++++ b/net/xfrm/xfrm_user.c
+@@ -2111,7 +2111,7 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
+       struct xfrm_usersa_flush *p = nlmsg_data(nlh);
+       int err;
+-      err = xfrm_state_flush(net, p->proto, true, false);
++      err = xfrm_state_flush(net, p->proto, true);
+       if (err) {
+               if (err == -ESRCH) /* empty table */
+                       return 0;
+-- 
+2.51.0
+
diff --git a/queue-5.10/series b/queue-5.10/series
new file mode 100644 (file)
index 0000000..7c20dd9
--- /dev/null
@@ -0,0 +1,4 @@
+xfrm-delete-x-tunnel-as-we-delete-x.patch
+revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch
+xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch
+xfrm-flush-all-states-in-xfrm_state_fini.patch
diff --git a/queue-5.10/xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch b/queue-5.10/xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch
new file mode 100644 (file)
index 0000000..e1f70f1
--- /dev/null
@@ -0,0 +1,76 @@
+From 09f729765a7d4e96cb0cb4fbd3a06e0c8c54a07e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 4 Dec 2025 17:43:27 -0500
+Subject: xfrm: also call xfrm_state_delete_tunnel at destroy time for states
+ that were never added
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit 10deb69864840ccf96b00ac2ab3a2055c0c04721 ]
+
+In commit b441cf3f8c4b ("xfrm: delete x->tunnel as we delete x"), I
+missed the case where state creation fails between full
+initialization (->init_state has been called) and being inserted on
+the lists.
+
+In this situation, ->init_state has been called, so for IPcomp
+tunnels, the fallback tunnel has been created and added onto the
+lists, but the user state never gets added, because we fail before
+that. The user state doesn't go through __xfrm_state_delete, so we
+don't call xfrm_state_delete_tunnel for those states, and we end up
+leaking the FB tunnel.
+
+There are several codepaths affected by this: the add/update paths, in
+both net/key and xfrm, and the migrate code (xfrm_migrate,
+xfrm_state_migrate). A "proper" rollback of the init_state work would
+probably be doable in the add/update code, but for migrate it gets
+more complicated as multiple states may be involved.
+
+At some point, the new (not-inserted) state will be destroyed, so call
+xfrm_state_delete_tunnel during xfrm_state_gc_destroy. Most states
+will have their fallback tunnel cleaned up during __xfrm_state_delete,
+which solves the issue that b441cf3f8c4b (and other patches before it)
+aimed at. All states (including FB tunnels) will be removed from the
+lists once xfrm_state_fini has called flush_work(&xfrm_state_gc_work).
+
+Reported-by: syzbot+999eb23467f83f9bf9bf@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=999eb23467f83f9bf9bf
+Fixes: b441cf3f8c4b ("xfrm: delete x->tunnel as we delete x")
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/xfrm/xfrm_state.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index a45d7e1dc5c6f..e13823d728127 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -476,6 +476,7 @@ void xfrm_state_free(struct xfrm_state *x)
+ }
+ EXPORT_SYMBOL(xfrm_state_free);
++static void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ static void xfrm_state_gc_destroy(struct xfrm_state *x)
+ {
+       hrtimer_cancel(&x->mtimer);
+@@ -490,6 +491,7 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
+       kfree(x->preplay_esn);
+       if (x->type_offload)
+               xfrm_put_type_offload(x->type_offload);
++      xfrm_state_delete_tunnel(x);
+       if (x->type) {
+               x->type->destructor(x);
+               xfrm_put_type(x->type);
+@@ -648,7 +650,6 @@ void __xfrm_state_destroy(struct xfrm_state *x)
+ }
+ EXPORT_SYMBOL(__xfrm_state_destroy);
+-static void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ int __xfrm_state_delete(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+-- 
+2.51.0
+
diff --git a/queue-5.10/xfrm-delete-x-tunnel-as-we-delete-x.patch b/queue-5.10/xfrm-delete-x-tunnel-as-we-delete-x.patch
new file mode 100644 (file)
index 0000000..4f5f353
--- /dev/null
@@ -0,0 +1,196 @@
+From 1e61827bf9a0e8112105c0d55c6e5372efc4e094 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 29 Jul 2025 17:11:52 -0400
+Subject: xfrm: delete x->tunnel as we delete x
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit b441cf3f8c4b8576639d20c8eb4aa32917602ecd ]
+
+The ipcomp fallback tunnels currently get deleted (from the various
+lists and hashtables) as the last user state that needed that fallback
+is destroyed (not deleted). If a reference to that user state still
+exists, the fallback state will remain on the hashtables/lists,
+triggering the WARN in xfrm_state_fini. Because of those remaining
+references, the fix in commit f75a2804da39 ("xfrm: destroy xfrm_state
+synchronously on net exit path") is not complete.
+
+We recently fixed one such situation in TCP due to defered freeing of
+skbs (commit 9b6412e6979f ("tcp: drop secpath at the same time as we
+currently drop dst")). This can also happen due to IP reassembly: skbs
+with a secpath remain on the reassembly queue until netns
+destruction. If we can't guarantee that the queues are flushed by the
+time xfrm_state_fini runs, there may still be references to a (user)
+xfrm_state, preventing the timely deletion of the corresponding
+fallback state.
+
+Instead of chasing each instance of skbs holding a secpath one by one,
+this patch fixes the issue directly within xfrm, by deleting the
+fallback state as soon as the last user state depending on it has been
+deleted. Destruction will still happen when the final reference is
+dropped.
+
+A separate lockdep class for the fallback state is required since
+we're going to lock x->tunnel while x is locked.
+
+Fixes: 9d4139c76905 ("netns xfrm: per-netns xfrm_state_all list")
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/xfrm.h      |  1 -
+ net/ipv4/ipcomp.c       |  2 ++
+ net/ipv6/ipcomp6.c      |  2 ++
+ net/ipv6/xfrm6_tunnel.c |  2 +-
+ net/xfrm/xfrm_ipcomp.c  |  1 -
+ net/xfrm/xfrm_state.c   | 19 ++++++++-----------
+ 6 files changed, 13 insertions(+), 14 deletions(-)
+
+diff --git a/include/net/xfrm.h b/include/net/xfrm.h
+index 2c1feca282036..a8584de9b18b7 100644
+--- a/include/net/xfrm.h
++++ b/include/net/xfrm.h
+@@ -400,7 +400,6 @@ int xfrm_input_register_afinfo(const struct xfrm_input_afinfo *afinfo);
+ int xfrm_input_unregister_afinfo(const struct xfrm_input_afinfo *afinfo);
+ void xfrm_flush_gc(void);
+-void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ struct xfrm_type {
+       char                    *description;
+diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
+index b42683212c659..1ebfab2607082 100644
+--- a/net/ipv4/ipcomp.c
++++ b/net/ipv4/ipcomp.c
+@@ -53,6 +53,7 @@ static int ipcomp4_err(struct sk_buff *skb, u32 info)
+ }
+ /* We always hold one tunnel user reference to indicate a tunnel */
++static struct lock_class_key xfrm_state_lock_key;
+ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+@@ -61,6 +62,7 @@ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
+       t = xfrm_state_alloc(net);
+       if (!t)
+               goto out;
++      lockdep_set_class(&t->lock, &xfrm_state_lock_key);
+       t->id.proto = IPPROTO_IPIP;
+       t->id.spi = x->props.saddr.a4;
+diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
+index daef890460b70..4bc0d4c0be147 100644
+--- a/net/ipv6/ipcomp6.c
++++ b/net/ipv6/ipcomp6.c
+@@ -71,6 +71,7 @@ static int ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+       return 0;
+ }
++static struct lock_class_key xfrm_state_lock_key;
+ static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+@@ -79,6 +80,7 @@ static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
+       t = xfrm_state_alloc(net);
+       if (!t)
+               goto out;
++      lockdep_set_class(&t->lock, &xfrm_state_lock_key);
+       t->id.proto = IPPROTO_IPV6;
+       t->id.spi = xfrm6_tunnel_alloc_spi(net, (xfrm_address_t *)&x->props.saddr);
+diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
+index f696d46e69100..61ffc01b6c479 100644
+--- a/net/ipv6/xfrm6_tunnel.c
++++ b/net/ipv6/xfrm6_tunnel.c
+@@ -331,8 +331,8 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
+       struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
+       unsigned int i;
+-      xfrm_flush_gc();
+       xfrm_state_flush(net, 0, false, true);
++      xfrm_flush_gc();
+       for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
+               WARN_ON_ONCE(!hlist_empty(&xfrm6_tn->spi_byaddr[i]));
+diff --git a/net/xfrm/xfrm_ipcomp.c b/net/xfrm/xfrm_ipcomp.c
+index 24ac6805275e9..5453c038e35a2 100644
+--- a/net/xfrm/xfrm_ipcomp.c
++++ b/net/xfrm/xfrm_ipcomp.c
+@@ -327,7 +327,6 @@ void ipcomp_destroy(struct xfrm_state *x)
+       struct ipcomp_data *ipcd = x->data;
+       if (!ipcd)
+               return;
+-      xfrm_state_delete_tunnel(x);
+       mutex_lock(&ipcomp_resource_mutex);
+       ipcomp_free_data(ipcd);
+       mutex_unlock(&ipcomp_resource_mutex);
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index da2d7012e5c74..3cd878a25602a 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -653,6 +653,7 @@ void __xfrm_state_destroy(struct xfrm_state *x, bool sync)
+ }
+ EXPORT_SYMBOL(__xfrm_state_destroy);
++static void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ int __xfrm_state_delete(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+@@ -674,6 +675,8 @@ int __xfrm_state_delete(struct xfrm_state *x)
+               xfrm_dev_state_delete(x);
++              xfrm_state_delete_tunnel(x);
++
+               /* All xfrm_state objects are created by xfrm_state_alloc.
+                * The xfrm_state_alloc call gives a reference, and that
+                * is what we are dropping here.
+@@ -777,10 +780,7 @@ int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync)
+                               err = xfrm_state_delete(x);
+                               xfrm_audit_state_delete(x, err ? 0 : 1,
+                                                       task_valid);
+-                              if (sync)
+-                                      xfrm_state_put_sync(x);
+-                              else
+-                                      xfrm_state_put(x);
++                              xfrm_state_put(x);
+                               if (!err)
+                                       cnt++;
+@@ -2535,20 +2535,17 @@ void xfrm_flush_gc(void)
+ }
+ EXPORT_SYMBOL(xfrm_flush_gc);
+-/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
+-void xfrm_state_delete_tunnel(struct xfrm_state *x)
++static void xfrm_state_delete_tunnel(struct xfrm_state *x)
+ {
+       if (x->tunnel) {
+               struct xfrm_state *t = x->tunnel;
+-              if (atomic_read(&t->tunnel_users) == 2)
++              if (atomic_dec_return(&t->tunnel_users) == 1)
+                       xfrm_state_delete(t);
+-              atomic_dec(&t->tunnel_users);
+-              xfrm_state_put_sync(t);
++              xfrm_state_put(t);
+               x->tunnel = NULL;
+       }
+ }
+-EXPORT_SYMBOL(xfrm_state_delete_tunnel);
+ u32 xfrm_state_mtu(struct xfrm_state *x, int mtu)
+ {
+@@ -2710,8 +2707,8 @@ void xfrm_state_fini(struct net *net)
+       unsigned int sz;
+       flush_work(&net->xfrm.state_hash_work);
+-      flush_work(&xfrm_state_gc_work);
+       xfrm_state_flush(net, 0, false, true);
++      flush_work(&xfrm_state_gc_work);
+       WARN_ON(!list_empty(&net->xfrm.state_all));
+-- 
+2.51.0
+
diff --git a/queue-5.10/xfrm-flush-all-states-in-xfrm_state_fini.patch b/queue-5.10/xfrm-flush-all-states-in-xfrm_state_fini.patch
new file mode 100644 (file)
index 0000000..fdb34be
--- /dev/null
@@ -0,0 +1,61 @@
+From 56280699b46b50fed736f8c31aaa9023b8326e11 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 4 Aug 2025 11:05:43 +0200
+Subject: xfrm: flush all states in xfrm_state_fini
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit 42e42562c9cfcdacf000f1b42284a4fad24f8546 ]
+
+While reverting commit f75a2804da39 ("xfrm: destroy xfrm_state
+synchronously on net exit path"), I incorrectly changed
+xfrm_state_flush's "proto" argument back to IPSEC_PROTO_ANY. This
+reverts some of the changes in commit dbb2483b2a46 ("xfrm: clean up
+xfrm protocol checks"), and leads to some states not being removed
+when we exit the netns.
+
+Pass 0 instead of IPSEC_PROTO_ANY from both xfrm_state_fini
+xfrm6_tunnel_net_exit, so that xfrm_state_flush deletes all states.
+
+Fixes: 2a198bbec691 ("Revert "xfrm: destroy xfrm_state synchronously on net exit path"")
+Reported-by: syzbot+6641a61fe0e2e89ae8c5@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=6641a61fe0e2e89ae8c5
+Tested-by: syzbot+6641a61fe0e2e89ae8c5@syzkaller.appspotmail.com
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv6/xfrm6_tunnel.c | 2 +-
+ net/xfrm/xfrm_state.c   | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
+index 70f9540937489..6de3cc3ba25d2 100644
+--- a/net/ipv6/xfrm6_tunnel.c
++++ b/net/ipv6/xfrm6_tunnel.c
+@@ -331,7 +331,7 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
+       struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
+       unsigned int i;
+-      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
++      xfrm_state_flush(net, 0, false);
+       xfrm_flush_gc();
+       for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index e13823d728127..b1243edf7f3a0 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -2703,7 +2703,7 @@ void xfrm_state_fini(struct net *net)
+       unsigned int sz;
+       flush_work(&net->xfrm.state_hash_work);
+-      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
++      xfrm_state_flush(net, 0, false);
+       flush_work(&xfrm_state_gc_work);
+       WARN_ON(!list_empty(&net->xfrm.state_all));
+-- 
+2.51.0
+
diff --git a/queue-5.15/dpaa2-mac-bail-if-the-dpmacs-fwnode-is-not-found.patch b/queue-5.15/dpaa2-mac-bail-if-the-dpmacs-fwnode-is-not-found.patch
new file mode 100644 (file)
index 0000000..f3bbe5e
--- /dev/null
@@ -0,0 +1,46 @@
+From ee4e760fa797daeec281f678d7a90dd5a695c831 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 6 Jan 2022 15:59:03 +0200
+Subject: dpaa2-mac: bail if the dpmacs fwnode is not found
+
+From: Robert-Ionut Alexa <robert-ionut.alexa@nxp.com>
+
+[ Upstream commit 5b1e38c0792cc7a44997328de37d393f81b2501a ]
+
+The parent pointer node handler must be declared with a NULL
+initializer. Before using it, a check must be performed to make
+sure that a valid address has been assigned to it.
+
+Signed-off-by: Robert-Ionut Alexa <robert-ionut.alexa@nxp.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
+index ae6d382d87352..4ace67bfa07c1 100644
+--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
+@@ -40,7 +40,7 @@ static int phy_mode(enum dpmac_eth_if eth_if, phy_interface_t *if_mode)
+ static struct fwnode_handle *dpaa2_mac_get_node(struct device *dev,
+                                               u16 dpmac_id)
+ {
+-      struct fwnode_handle *fwnode, *parent, *child  = NULL;
++      struct fwnode_handle *fwnode, *parent = NULL, *child  = NULL;
+       struct device_node *dpmacs = NULL;
+       int err;
+       u32 id;
+@@ -55,6 +55,9 @@ static struct fwnode_handle *dpaa2_mac_get_node(struct device *dev,
+               parent = fwnode;
+       }
++      if (!parent)
++              return NULL;
++
+       fwnode_for_each_child_node(parent, child) {
+               err = -EINVAL;
+               if (is_acpi_device_node(child))
+-- 
+2.51.0
+
diff --git a/queue-5.15/drm-i915-selftests-fix-inconsistent-is_err-and-ptr_e.patch b/queue-5.15/drm-i915-selftests-fix-inconsistent-is_err-and-ptr_e.patch
new file mode 100644 (file)
index 0000000..2856e3b
--- /dev/null
@@ -0,0 +1,45 @@
+From 48589575b8d4770017fdd187f7fed135d5d7455c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 Oct 2021 20:06:55 +0800
+Subject: drm/i915/selftests: Fix inconsistent IS_ERR and PTR_ERR
+
+From: Kai Song <songkai01@inspur.com>
+
+[ Upstream commit fc7bf4c0d65a342b29fe38c332db3fe900b481b9 ]
+
+Fix inconsistent IS_ERR and PTR_ERR in i915_gem_dmabuf.c
+
+Signed-off-by: Kai Song <songkai01@inspur.com>
+Reviewed-by: Matthew Auld <matthew.auld@intel.com>
+Signed-off-by: Matthew Auld <matthew.auld@intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20211022120655.22173-1-songkai01@inspur.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c
+index 4a6bb64c3a354..3cc74b0fed068 100644
+--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c
++++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c
+@@ -102,7 +102,7 @@ static int igt_dmabuf_import_same_driver_lmem(void *arg)
+       obj = __i915_gem_object_create_user(i915, PAGE_SIZE, &lmem, 1);
+       if (IS_ERR(obj)) {
+               pr_err("__i915_gem_object_create_user failed with err=%ld\n",
+-                     PTR_ERR(dmabuf));
++                     PTR_ERR(obj));
+               err = PTR_ERR(obj);
+               goto out_ret;
+       }
+@@ -158,7 +158,7 @@ static int igt_dmabuf_import_same_driver(struct drm_i915_private *i915,
+                                           regions, num_regions);
+       if (IS_ERR(obj)) {
+               pr_err("__i915_gem_object_create_user failed with err=%ld\n",
+-                     PTR_ERR(dmabuf));
++                     PTR_ERR(obj));
+               err = PTR_ERR(obj);
+               goto out_ret;
+       }
+-- 
+2.51.0
+
diff --git a/queue-5.15/leds-replace-all-non-returning-strlcpy-with-strscpy.patch b/queue-5.15/leds-replace-all-non-returning-strlcpy-with-strscpy.patch
new file mode 100644 (file)
index 0000000..7df45a1
--- /dev/null
@@ -0,0 +1,74 @@
+From 7f92f74149f6a5530bb9717e067a6611d1575c0b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 23 May 2023 02:14:51 +0000
+Subject: leds: Replace all non-returning strlcpy with strscpy
+
+From: Azeem Shaikh <azeemshaikh38@gmail.com>
+
+[ Upstream commit bf4a35e9201d30b63a8d276797d6ecfaa596ccd3 ]
+
+strlcpy() reads the entire source buffer first.
+This read may exceed the destination size limit.
+This is both inefficient and can lead to linear read
+overflows if a source string is not NUL-terminated [1].
+In an effort to remove strlcpy() completely [2], replace
+strlcpy() here with strscpy().
+No return values were used, so direct replacement is safe.
+
+[1] https://www.kernel.org/doc/html/latest/process/deprecated.html#strlcpy
+[2] https://github.com/KSPP/linux/issues/89
+
+Signed-off-by: Azeem Shaikh <azeemshaikh38@gmail.com>
+Reviewed-by: Kees Cook <keescook@chromium.org>
+Link: https://lore.kernel.org/r/20230523021451.2406362-1-azeemshaikh38@gmail.com
+Signed-off-by: Lee Jones <lee@kernel.org>
+Stable-dep-of: ccc35ff2fd29 ("leds: spi-byte: Use devm_led_classdev_register_ext()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/leds/flash/leds-aat1290.c | 2 +-
+ drivers/leds/led-class.c          | 2 +-
+ drivers/leds/leds-spi-byte.c      | 2 +-
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/leds/flash/leds-aat1290.c b/drivers/leds/flash/leds-aat1290.c
+index 589484b22c796..f12ecb2c65803 100644
+--- a/drivers/leds/flash/leds-aat1290.c
++++ b/drivers/leds/flash/leds-aat1290.c
+@@ -425,7 +425,7 @@ static void aat1290_init_v4l2_flash_config(struct aat1290_led *led,
+       struct led_classdev *led_cdev = &led->fled_cdev.led_cdev;
+       struct led_flash_setting *s;
+-      strlcpy(v4l2_sd_cfg->dev_name, led_cdev->dev->kobj.name,
++      strscpy(v4l2_sd_cfg->dev_name, led_cdev->dev->kobj.name,
+               sizeof(v4l2_sd_cfg->dev_name));
+       s = &v4l2_sd_cfg->intensity;
+diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
+index 1e4fed64aee18..e098e001a7b0b 100644
+--- a/drivers/leds/led-class.c
++++ b/drivers/leds/led-class.c
+@@ -321,7 +321,7 @@ static int led_classdev_next_name(const char *init_name, char *name,
+       int ret = 0;
+       struct device *dev;
+-      strlcpy(name, init_name, len);
++      strscpy(name, init_name, len);
+       while ((ret < len) &&
+              (dev = class_find_device_by_name(leds_class, name))) {
+diff --git a/drivers/leds/leds-spi-byte.c b/drivers/leds/leds-spi-byte.c
+index 82696e0607a53..958e898b58d09 100644
+--- a/drivers/leds/leds-spi-byte.c
++++ b/drivers/leds/leds-spi-byte.c
+@@ -97,7 +97,7 @@ static int spi_byte_probe(struct spi_device *spi)
+               return -ENOMEM;
+       of_property_read_string(child, "label", &name);
+-      strlcpy(led->name, name, sizeof(led->name));
++      strscpy(led->name, name, sizeof(led->name));
+       led->spi = spi;
+       mutex_init(&led->mutex);
+       led->cdef = device_get_match_data(dev);
+-- 
+2.51.0
+
diff --git a/queue-5.15/leds-spi-byte-use-devm_led_classdev_register_ext.patch b/queue-5.15/leds-spi-byte-use-devm_led_classdev_register_ext.patch
new file mode 100644 (file)
index 0000000..40da6c4
--- /dev/null
@@ -0,0 +1,63 @@
+From bcf199dd00fe97ac38da15a3a80b92418801c895 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 4 Feb 2024 16:07:26 +0100
+Subject: leds: spi-byte: Use devm_led_classdev_register_ext()
+
+From: Stefan Kalscheuer <stefan@stklcode.de>
+
+[ Upstream commit ccc35ff2fd2911986b716a87fe65e03fac2312c9 ]
+
+Use extended classdev registration to generate generic device names from
+color and function enums instead of reading only the label from the
+device tree.
+
+Signed-off-by: Stefan Kalscheuer <stefan@stklcode.de>
+Link: https://lore.kernel.org/r/20240204150726.29783-1-stefan@stklcode.de
+Signed-off-by: Lee Jones <lee@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/leds/leds-spi-byte.c | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/leds/leds-spi-byte.c b/drivers/leds/leds-spi-byte.c
+index 958e898b58d09..9a17424fd2da8 100644
+--- a/drivers/leds/leds-spi-byte.c
++++ b/drivers/leds/leds-spi-byte.c
+@@ -83,7 +83,7 @@ static int spi_byte_probe(struct spi_device *spi)
+       struct device_node *child;
+       struct device *dev = &spi->dev;
+       struct spi_byte_led *led;
+-      const char *name = "leds-spi-byte::";
++      struct led_init_data init_data = {};
+       const char *state;
+       int ret;
+@@ -96,12 +96,9 @@ static int spi_byte_probe(struct spi_device *spi)
+       if (!led)
+               return -ENOMEM;
+-      of_property_read_string(child, "label", &name);
+-      strscpy(led->name, name, sizeof(led->name));
+       led->spi = spi;
+       mutex_init(&led->mutex);
+       led->cdef = device_get_match_data(dev);
+-      led->ldev.name = led->name;
+       led->ldev.brightness = LED_OFF;
+       led->ldev.max_brightness = led->cdef->max_value - led->cdef->off_value;
+       led->ldev.brightness_set_blocking = spi_byte_brightness_set_blocking;
+@@ -121,7 +118,11 @@ static int spi_byte_probe(struct spi_device *spi)
+       spi_byte_brightness_set_blocking(&led->ldev,
+                                        led->ldev.brightness);
+-      ret = devm_led_classdev_register(&spi->dev, &led->ldev);
++      init_data.fwnode = of_fwnode_handle(child);
++      init_data.devicename = "leds-spi-byte";
++      init_data.default_label = ":";
++
++      ret = devm_led_classdev_register_ext(&spi->dev, &led->ldev, &init_data);
+       if (ret) {
+               of_node_put(child);
+               mutex_destroy(&led->mutex);
+-- 
+2.51.0
+
diff --git a/queue-5.15/revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch b/queue-5.15/revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch
new file mode 100644 (file)
index 0000000..d80d349
--- /dev/null
@@ -0,0 +1,176 @@
+From d237c56c176110ead253a5da1f28c4b83dd70d18 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 29 Jul 2025 17:11:53 -0400
+Subject: Revert "xfrm: destroy xfrm_state synchronously on net exit path"
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit 2a198bbec6913ae1c90ec963750003c6213668c7 ]
+
+This reverts commit f75a2804da391571563c4b6b29e7797787332673.
+
+With all states (whether user or kern) removed from the hashtables
+during deletion, there's no need for synchronous destruction of
+states. xfrm6_tunnel states still need to have been destroyed (which
+will be the case when its last user is deleted (not destroyed)) so
+that xfrm6_tunnel_free_spi removes it from the per-netns hashtable
+before the netns is destroyed.
+
+This has the benefit of skipping one synchronize_rcu per state (in
+__xfrm_state_destroy(sync=true)) when we exit a netns.
+
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/xfrm.h      | 12 +++---------
+ net/ipv6/xfrm6_tunnel.c |  2 +-
+ net/key/af_key.c        |  2 +-
+ net/xfrm/xfrm_state.c   | 23 +++++++++--------------
+ net/xfrm/xfrm_user.c    |  2 +-
+ 5 files changed, 15 insertions(+), 26 deletions(-)
+
+diff --git a/include/net/xfrm.h b/include/net/xfrm.h
+index 3e1690e0a38de..875f7dc4706ef 100644
+--- a/include/net/xfrm.h
++++ b/include/net/xfrm.h
+@@ -785,7 +785,7 @@ static inline void xfrm_pols_put(struct xfrm_policy **pols, int npols)
+               xfrm_pol_put(pols[i]);
+ }
+-void __xfrm_state_destroy(struct xfrm_state *, bool);
++void __xfrm_state_destroy(struct xfrm_state *);
+ static inline void __xfrm_state_put(struct xfrm_state *x)
+ {
+@@ -795,13 +795,7 @@ static inline void __xfrm_state_put(struct xfrm_state *x)
+ static inline void xfrm_state_put(struct xfrm_state *x)
+ {
+       if (refcount_dec_and_test(&x->refcnt))
+-              __xfrm_state_destroy(x, false);
+-}
+-
+-static inline void xfrm_state_put_sync(struct xfrm_state *x)
+-{
+-      if (refcount_dec_and_test(&x->refcnt))
+-              __xfrm_state_destroy(x, true);
++              __xfrm_state_destroy(x);
+ }
+ static inline void xfrm_state_hold(struct xfrm_state *x)
+@@ -1577,7 +1571,7 @@ struct xfrmk_spdinfo {
+ struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq);
+ int xfrm_state_delete(struct xfrm_state *x);
+-int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync);
++int xfrm_state_flush(struct net *net, u8 proto, bool task_valid);
+ int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid);
+ void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
+ void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
+diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
+index 34f500a417fa6..3d811248f3129 100644
+--- a/net/ipv6/xfrm6_tunnel.c
++++ b/net/ipv6/xfrm6_tunnel.c
+@@ -330,7 +330,7 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
+       struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
+       unsigned int i;
+-      xfrm_state_flush(net, 0, false, true);
++      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
+       xfrm_flush_gc();
+       for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
+diff --git a/net/key/af_key.c b/net/key/af_key.c
+index 258fa046f440d..925fe4f89966b 100644
+--- a/net/key/af_key.c
++++ b/net/key/af_key.c
+@@ -1766,7 +1766,7 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_m
+       if (proto == 0)
+               return -EINVAL;
+-      err = xfrm_state_flush(net, proto, true, false);
++      err = xfrm_state_flush(net, proto, true);
+       err2 = unicast_flush_resp(sk, hdr);
+       if (err || err2) {
+               if (err == -ESRCH) /* empty table - go quietly */
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index 6e410f30feba7..0e2e13c62e6b7 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -497,7 +497,7 @@ void xfrm_state_free(struct xfrm_state *x)
+ }
+ EXPORT_SYMBOL(xfrm_state_free);
+-static void ___xfrm_state_destroy(struct xfrm_state *x)
++static void xfrm_state_gc_destroy(struct xfrm_state *x)
+ {
+       hrtimer_cancel(&x->mtimer);
+       del_timer_sync(&x->rtimer);
+@@ -535,7 +535,7 @@ static void xfrm_state_gc_task(struct work_struct *work)
+       synchronize_rcu();
+       hlist_for_each_entry_safe(x, tmp, &gc_list, gclist)
+-              ___xfrm_state_destroy(x);
++              xfrm_state_gc_destroy(x);
+ }
+ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
+@@ -659,19 +659,14 @@ struct xfrm_state *xfrm_state_alloc(struct net *net)
+ }
+ EXPORT_SYMBOL(xfrm_state_alloc);
+-void __xfrm_state_destroy(struct xfrm_state *x, bool sync)
++void __xfrm_state_destroy(struct xfrm_state *x)
+ {
+       WARN_ON(x->km.state != XFRM_STATE_DEAD);
+-      if (sync) {
+-              synchronize_rcu();
+-              ___xfrm_state_destroy(x);
+-      } else {
+-              spin_lock_bh(&xfrm_state_gc_lock);
+-              hlist_add_head(&x->gclist, &xfrm_state_gc_list);
+-              spin_unlock_bh(&xfrm_state_gc_lock);
+-              schedule_work(&xfrm_state_gc_work);
+-      }
++      spin_lock_bh(&xfrm_state_gc_lock);
++      hlist_add_head(&x->gclist, &xfrm_state_gc_list);
++      spin_unlock_bh(&xfrm_state_gc_lock);
++      schedule_work(&xfrm_state_gc_work);
+ }
+ EXPORT_SYMBOL(__xfrm_state_destroy);
+@@ -782,7 +777,7 @@ xfrm_dev_state_flush_secctx_check(struct net *net, struct net_device *dev, bool
+ }
+ #endif
+-int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync)
++int xfrm_state_flush(struct net *net, u8 proto, bool task_valid)
+ {
+       int i, err = 0, cnt = 0;
+@@ -2745,7 +2740,7 @@ void xfrm_state_fini(struct net *net)
+       unsigned int sz;
+       flush_work(&net->xfrm.state_hash_work);
+-      xfrm_state_flush(net, 0, false, true);
++      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
+       flush_work(&xfrm_state_gc_work);
+       WARN_ON(!list_empty(&net->xfrm.state_all));
+diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
+index 1aa05b608ccf0..d9238e17ab427 100644
+--- a/net/xfrm/xfrm_user.c
++++ b/net/xfrm/xfrm_user.c
+@@ -2210,7 +2210,7 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
+       struct xfrm_usersa_flush *p = nlmsg_data(nlh);
+       int err;
+-      err = xfrm_state_flush(net, p->proto, true, false);
++      err = xfrm_state_flush(net, p->proto, true);
+       if (err) {
+               if (err == -ESRCH) /* empty table */
+                       return 0;
+-- 
+2.51.0
+
diff --git a/queue-5.15/series b/queue-5.15/series
new file mode 100644 (file)
index 0000000..dfa26f0
--- /dev/null
@@ -0,0 +1,8 @@
+xfrm-delete-x-tunnel-as-we-delete-x.patch
+revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch
+xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch
+xfrm-flush-all-states-in-xfrm_state_fini.patch
+dpaa2-mac-bail-if-the-dpmacs-fwnode-is-not-found.patch
+drm-i915-selftests-fix-inconsistent-is_err-and-ptr_e.patch
+leds-replace-all-non-returning-strlcpy-with-strscpy.patch
+leds-spi-byte-use-devm_led_classdev_register_ext.patch
diff --git a/queue-5.15/xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch b/queue-5.15/xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch
new file mode 100644 (file)
index 0000000..96f0672
--- /dev/null
@@ -0,0 +1,76 @@
+From 51bdf2f7b8e8b3310ce8026930646befef5c35c5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 4 Dec 2025 17:43:27 -0500
+Subject: xfrm: also call xfrm_state_delete_tunnel at destroy time for states
+ that were never added
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit 10deb69864840ccf96b00ac2ab3a2055c0c04721 ]
+
+In commit b441cf3f8c4b ("xfrm: delete x->tunnel as we delete x"), I
+missed the case where state creation fails between full
+initialization (->init_state has been called) and being inserted on
+the lists.
+
+In this situation, ->init_state has been called, so for IPcomp
+tunnels, the fallback tunnel has been created and added onto the
+lists, but the user state never gets added, because we fail before
+that. The user state doesn't go through __xfrm_state_delete, so we
+don't call xfrm_state_delete_tunnel for those states, and we end up
+leaking the FB tunnel.
+
+There are several codepaths affected by this: the add/update paths, in
+both net/key and xfrm, and the migrate code (xfrm_migrate,
+xfrm_state_migrate). A "proper" rollback of the init_state work would
+probably be doable in the add/update code, but for migrate it gets
+more complicated as multiple states may be involved.
+
+At some point, the new (not-inserted) state will be destroyed, so call
+xfrm_state_delete_tunnel during xfrm_state_gc_destroy. Most states
+will have their fallback tunnel cleaned up during __xfrm_state_delete,
+which solves the issue that b441cf3f8c4b (and other patches before it)
+aimed at. All states (including FB tunnels) will be removed from the
+lists once xfrm_state_fini has called flush_work(&xfrm_state_gc_work).
+
+Reported-by: syzbot+999eb23467f83f9bf9bf@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=999eb23467f83f9bf9bf
+Fixes: b441cf3f8c4b ("xfrm: delete x->tunnel as we delete x")
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/xfrm/xfrm_state.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index 0e2e13c62e6b7..8287dc73e839d 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -497,6 +497,7 @@ void xfrm_state_free(struct xfrm_state *x)
+ }
+ EXPORT_SYMBOL(xfrm_state_free);
++static void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ static void xfrm_state_gc_destroy(struct xfrm_state *x)
+ {
+       hrtimer_cancel(&x->mtimer);
+@@ -511,6 +512,7 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
+       kfree(x->preplay_esn);
+       if (x->type_offload)
+               xfrm_put_type_offload(x->type_offload);
++      xfrm_state_delete_tunnel(x);
+       if (x->type) {
+               x->type->destructor(x);
+               xfrm_put_type(x->type);
+@@ -670,7 +672,6 @@ void __xfrm_state_destroy(struct xfrm_state *x)
+ }
+ EXPORT_SYMBOL(__xfrm_state_destroy);
+-static void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ int __xfrm_state_delete(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+-- 
+2.51.0
+
diff --git a/queue-5.15/xfrm-delete-x-tunnel-as-we-delete-x.patch b/queue-5.15/xfrm-delete-x-tunnel-as-we-delete-x.patch
new file mode 100644 (file)
index 0000000..d3f2282
--- /dev/null
@@ -0,0 +1,196 @@
+From caebe95cf553ddecc89757267988c5f9f1812862 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 29 Jul 2025 17:11:52 -0400
+Subject: xfrm: delete x->tunnel as we delete x
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit b441cf3f8c4b8576639d20c8eb4aa32917602ecd ]
+
+The ipcomp fallback tunnels currently get deleted (from the various
+lists and hashtables) as the last user state that needed that fallback
+is destroyed (not deleted). If a reference to that user state still
+exists, the fallback state will remain on the hashtables/lists,
+triggering the WARN in xfrm_state_fini. Because of those remaining
+references, the fix in commit f75a2804da39 ("xfrm: destroy xfrm_state
+synchronously on net exit path") is not complete.
+
+We recently fixed one such situation in TCP due to defered freeing of
+skbs (commit 9b6412e6979f ("tcp: drop secpath at the same time as we
+currently drop dst")). This can also happen due to IP reassembly: skbs
+with a secpath remain on the reassembly queue until netns
+destruction. If we can't guarantee that the queues are flushed by the
+time xfrm_state_fini runs, there may still be references to a (user)
+xfrm_state, preventing the timely deletion of the corresponding
+fallback state.
+
+Instead of chasing each instance of skbs holding a secpath one by one,
+this patch fixes the issue directly within xfrm, by deleting the
+fallback state as soon as the last user state depending on it has been
+deleted. Destruction will still happen when the final reference is
+dropped.
+
+A separate lockdep class for the fallback state is required since
+we're going to lock x->tunnel while x is locked.
+
+Fixes: 9d4139c76905 ("netns xfrm: per-netns xfrm_state_all list")
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/xfrm.h      |  1 -
+ net/ipv4/ipcomp.c       |  2 ++
+ net/ipv6/ipcomp6.c      |  2 ++
+ net/ipv6/xfrm6_tunnel.c |  2 +-
+ net/xfrm/xfrm_ipcomp.c  |  1 -
+ net/xfrm/xfrm_state.c   | 19 ++++++++-----------
+ 6 files changed, 13 insertions(+), 14 deletions(-)
+
+diff --git a/include/net/xfrm.h b/include/net/xfrm.h
+index 3232cdf1b4ef4..3e1690e0a38de 100644
+--- a/include/net/xfrm.h
++++ b/include/net/xfrm.h
+@@ -394,7 +394,6 @@ int xfrm_input_register_afinfo(const struct xfrm_input_afinfo *afinfo);
+ int xfrm_input_unregister_afinfo(const struct xfrm_input_afinfo *afinfo);
+ void xfrm_flush_gc(void);
+-void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ struct xfrm_type {
+       struct module           *owner;
+diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
+index 366094c1ce6ca..f9f8ed65e1ec9 100644
+--- a/net/ipv4/ipcomp.c
++++ b/net/ipv4/ipcomp.c
+@@ -54,6 +54,7 @@ static int ipcomp4_err(struct sk_buff *skb, u32 info)
+ }
+ /* We always hold one tunnel user reference to indicate a tunnel */
++static struct lock_class_key xfrm_state_lock_key;
+ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+@@ -62,6 +63,7 @@ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
+       t = xfrm_state_alloc(net);
+       if (!t)
+               goto out;
++      lockdep_set_class(&t->lock, &xfrm_state_lock_key);
+       t->id.proto = IPPROTO_IPIP;
+       t->id.spi = x->props.saddr.a4;
+diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
+index 15f984be35705..faa1ac5a9715e 100644
+--- a/net/ipv6/ipcomp6.c
++++ b/net/ipv6/ipcomp6.c
+@@ -71,6 +71,7 @@ static int ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+       return 0;
+ }
++static struct lock_class_key xfrm_state_lock_key;
+ static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+@@ -79,6 +80,7 @@ static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
+       t = xfrm_state_alloc(net);
+       if (!t)
+               goto out;
++      lockdep_set_class(&t->lock, &xfrm_state_lock_key);
+       t->id.proto = IPPROTO_IPV6;
+       t->id.spi = xfrm6_tunnel_alloc_spi(net, (xfrm_address_t *)&x->props.saddr);
+diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
+index 2b31112c0856b..34f500a417fa6 100644
+--- a/net/ipv6/xfrm6_tunnel.c
++++ b/net/ipv6/xfrm6_tunnel.c
+@@ -330,8 +330,8 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
+       struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
+       unsigned int i;
+-      xfrm_flush_gc();
+       xfrm_state_flush(net, 0, false, true);
++      xfrm_flush_gc();
+       for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
+               WARN_ON_ONCE(!hlist_empty(&xfrm6_tn->spi_byaddr[i]));
+diff --git a/net/xfrm/xfrm_ipcomp.c b/net/xfrm/xfrm_ipcomp.c
+index 92ad336a83ab5..342e7bc2804ee 100644
+--- a/net/xfrm/xfrm_ipcomp.c
++++ b/net/xfrm/xfrm_ipcomp.c
+@@ -318,7 +318,6 @@ void ipcomp_destroy(struct xfrm_state *x)
+       struct ipcomp_data *ipcd = x->data;
+       if (!ipcd)
+               return;
+-      xfrm_state_delete_tunnel(x);
+       mutex_lock(&ipcomp_resource_mutex);
+       ipcomp_free_data(ipcd);
+       mutex_unlock(&ipcomp_resource_mutex);
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index c1bc5d780f640..6e410f30feba7 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -675,6 +675,7 @@ void __xfrm_state_destroy(struct xfrm_state *x, bool sync)
+ }
+ EXPORT_SYMBOL(__xfrm_state_destroy);
++static void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ int __xfrm_state_delete(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+@@ -698,6 +699,8 @@ int __xfrm_state_delete(struct xfrm_state *x)
+               xfrm_dev_state_delete(x);
++              xfrm_state_delete_tunnel(x);
++
+               /* All xfrm_state objects are created by xfrm_state_alloc.
+                * The xfrm_state_alloc call gives a reference, and that
+                * is what we are dropping here.
+@@ -801,10 +804,7 @@ int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync)
+                               err = xfrm_state_delete(x);
+                               xfrm_audit_state_delete(x, err ? 0 : 1,
+                                                       task_valid);
+-                              if (sync)
+-                                      xfrm_state_put_sync(x);
+-                              else
+-                                      xfrm_state_put(x);
++                              xfrm_state_put(x);
+                               if (!err)
+                                       cnt++;
+@@ -2567,20 +2567,17 @@ void xfrm_flush_gc(void)
+ }
+ EXPORT_SYMBOL(xfrm_flush_gc);
+-/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
+-void xfrm_state_delete_tunnel(struct xfrm_state *x)
++static void xfrm_state_delete_tunnel(struct xfrm_state *x)
+ {
+       if (x->tunnel) {
+               struct xfrm_state *t = x->tunnel;
+-              if (atomic_read(&t->tunnel_users) == 2)
++              if (atomic_dec_return(&t->tunnel_users) == 1)
+                       xfrm_state_delete(t);
+-              atomic_dec(&t->tunnel_users);
+-              xfrm_state_put_sync(t);
++              xfrm_state_put(t);
+               x->tunnel = NULL;
+       }
+ }
+-EXPORT_SYMBOL(xfrm_state_delete_tunnel);
+ u32 xfrm_state_mtu(struct xfrm_state *x, int mtu)
+ {
+@@ -2748,8 +2745,8 @@ void xfrm_state_fini(struct net *net)
+       unsigned int sz;
+       flush_work(&net->xfrm.state_hash_work);
+-      flush_work(&xfrm_state_gc_work);
+       xfrm_state_flush(net, 0, false, true);
++      flush_work(&xfrm_state_gc_work);
+       WARN_ON(!list_empty(&net->xfrm.state_all));
+-- 
+2.51.0
+
diff --git a/queue-5.15/xfrm-flush-all-states-in-xfrm_state_fini.patch b/queue-5.15/xfrm-flush-all-states-in-xfrm_state_fini.patch
new file mode 100644 (file)
index 0000000..ada5434
--- /dev/null
@@ -0,0 +1,61 @@
+From 46a9bacfcce9e10c16812ca14602fe4c9e91f622 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 4 Aug 2025 11:05:43 +0200
+Subject: xfrm: flush all states in xfrm_state_fini
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit 42e42562c9cfcdacf000f1b42284a4fad24f8546 ]
+
+While reverting commit f75a2804da39 ("xfrm: destroy xfrm_state
+synchronously on net exit path"), I incorrectly changed
+xfrm_state_flush's "proto" argument back to IPSEC_PROTO_ANY. This
+reverts some of the changes in commit dbb2483b2a46 ("xfrm: clean up
+xfrm protocol checks"), and leads to some states not being removed
+when we exit the netns.
+
+Pass 0 instead of IPSEC_PROTO_ANY from both xfrm_state_fini
+xfrm6_tunnel_net_exit, so that xfrm_state_flush deletes all states.
+
+Fixes: 2a198bbec691 ("Revert "xfrm: destroy xfrm_state synchronously on net exit path"")
+Reported-by: syzbot+6641a61fe0e2e89ae8c5@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=6641a61fe0e2e89ae8c5
+Tested-by: syzbot+6641a61fe0e2e89ae8c5@syzkaller.appspotmail.com
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv6/xfrm6_tunnel.c | 2 +-
+ net/xfrm/xfrm_state.c   | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
+index 3d811248f3129..a3e6860406fcb 100644
+--- a/net/ipv6/xfrm6_tunnel.c
++++ b/net/ipv6/xfrm6_tunnel.c
+@@ -330,7 +330,7 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
+       struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
+       unsigned int i;
+-      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
++      xfrm_state_flush(net, 0, false);
+       xfrm_flush_gc();
+       for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index 8287dc73e839d..54ae99f69f25f 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -2741,7 +2741,7 @@ void xfrm_state_fini(struct net *net)
+       unsigned int sz;
+       flush_work(&net->xfrm.state_hash_work);
+-      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
++      xfrm_state_flush(net, 0, false);
+       flush_work(&xfrm_state_gc_work);
+       WARN_ON(!list_empty(&net->xfrm.state_all));
+-- 
+2.51.0
+
diff --git a/queue-6.1/leds-replace-all-non-returning-strlcpy-with-strscpy.patch b/queue-6.1/leds-replace-all-non-returning-strlcpy-with-strscpy.patch
new file mode 100644 (file)
index 0000000..a905758
--- /dev/null
@@ -0,0 +1,74 @@
+From a8b62e8c40f95a1dff2b0dc08fa84663e1bb939a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 23 May 2023 02:14:51 +0000
+Subject: leds: Replace all non-returning strlcpy with strscpy
+
+From: Azeem Shaikh <azeemshaikh38@gmail.com>
+
+[ Upstream commit bf4a35e9201d30b63a8d276797d6ecfaa596ccd3 ]
+
+strlcpy() reads the entire source buffer first.
+This read may exceed the destination size limit.
+This is both inefficient and can lead to linear read
+overflows if a source string is not NUL-terminated [1].
+In an effort to remove strlcpy() completely [2], replace
+strlcpy() here with strscpy().
+No return values were used, so direct replacement is safe.
+
+[1] https://www.kernel.org/doc/html/latest/process/deprecated.html#strlcpy
+[2] https://github.com/KSPP/linux/issues/89
+
+Signed-off-by: Azeem Shaikh <azeemshaikh38@gmail.com>
+Reviewed-by: Kees Cook <keescook@chromium.org>
+Link: https://lore.kernel.org/r/20230523021451.2406362-1-azeemshaikh38@gmail.com
+Signed-off-by: Lee Jones <lee@kernel.org>
+Stable-dep-of: ccc35ff2fd29 ("leds: spi-byte: Use devm_led_classdev_register_ext()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/leds/flash/leds-aat1290.c | 2 +-
+ drivers/leds/led-class.c          | 2 +-
+ drivers/leds/leds-spi-byte.c      | 2 +-
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/leds/flash/leds-aat1290.c b/drivers/leds/flash/leds-aat1290.c
+index 589484b22c796..f12ecb2c65803 100644
+--- a/drivers/leds/flash/leds-aat1290.c
++++ b/drivers/leds/flash/leds-aat1290.c
+@@ -425,7 +425,7 @@ static void aat1290_init_v4l2_flash_config(struct aat1290_led *led,
+       struct led_classdev *led_cdev = &led->fled_cdev.led_cdev;
+       struct led_flash_setting *s;
+-      strlcpy(v4l2_sd_cfg->dev_name, led_cdev->dev->kobj.name,
++      strscpy(v4l2_sd_cfg->dev_name, led_cdev->dev->kobj.name,
+               sizeof(v4l2_sd_cfg->dev_name));
+       s = &v4l2_sd_cfg->intensity;
+diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
+index 93fdca5c7dc5d..923138c808ca2 100644
+--- a/drivers/leds/led-class.c
++++ b/drivers/leds/led-class.c
+@@ -321,7 +321,7 @@ static int led_classdev_next_name(const char *init_name, char *name,
+       int ret = 0;
+       struct device *dev;
+-      strlcpy(name, init_name, len);
++      strscpy(name, init_name, len);
+       while ((ret < len) &&
+              (dev = class_find_device_by_name(leds_class, name))) {
+diff --git a/drivers/leds/leds-spi-byte.c b/drivers/leds/leds-spi-byte.c
+index 6883d3ba382f9..065a2bcb7c14b 100644
+--- a/drivers/leds/leds-spi-byte.c
++++ b/drivers/leds/leds-spi-byte.c
+@@ -97,7 +97,7 @@ static int spi_byte_probe(struct spi_device *spi)
+               return -ENOMEM;
+       of_property_read_string(child, "label", &name);
+-      strlcpy(led->name, name, sizeof(led->name));
++      strscpy(led->name, name, sizeof(led->name));
+       led->spi = spi;
+       mutex_init(&led->mutex);
+       led->cdef = device_get_match_data(dev);
+-- 
+2.51.0
+
diff --git a/queue-6.1/leds-spi-byte-use-devm_led_classdev_register_ext.patch b/queue-6.1/leds-spi-byte-use-devm_led_classdev_register_ext.patch
new file mode 100644 (file)
index 0000000..b59be4d
--- /dev/null
@@ -0,0 +1,63 @@
+From cd098b38c929b2f011203473c3962fb344122e43 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 4 Feb 2024 16:07:26 +0100
+Subject: leds: spi-byte: Use devm_led_classdev_register_ext()
+
+From: Stefan Kalscheuer <stefan@stklcode.de>
+
+[ Upstream commit ccc35ff2fd2911986b716a87fe65e03fac2312c9 ]
+
+Use extended classdev registration to generate generic device names from
+color and function enums instead of reading only the label from the
+device tree.
+
+Signed-off-by: Stefan Kalscheuer <stefan@stklcode.de>
+Link: https://lore.kernel.org/r/20240204150726.29783-1-stefan@stklcode.de
+Signed-off-by: Lee Jones <lee@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/leds/leds-spi-byte.c | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/leds/leds-spi-byte.c b/drivers/leds/leds-spi-byte.c
+index 065a2bcb7c14b..eb6481df5997f 100644
+--- a/drivers/leds/leds-spi-byte.c
++++ b/drivers/leds/leds-spi-byte.c
+@@ -83,7 +83,7 @@ static int spi_byte_probe(struct spi_device *spi)
+       struct device_node *child;
+       struct device *dev = &spi->dev;
+       struct spi_byte_led *led;
+-      const char *name = "leds-spi-byte::";
++      struct led_init_data init_data = {};
+       const char *state;
+       int ret;
+@@ -96,12 +96,9 @@ static int spi_byte_probe(struct spi_device *spi)
+       if (!led)
+               return -ENOMEM;
+-      of_property_read_string(child, "label", &name);
+-      strscpy(led->name, name, sizeof(led->name));
+       led->spi = spi;
+       mutex_init(&led->mutex);
+       led->cdef = device_get_match_data(dev);
+-      led->ldev.name = led->name;
+       led->ldev.brightness = LED_OFF;
+       led->ldev.max_brightness = led->cdef->max_value - led->cdef->off_value;
+       led->ldev.brightness_set_blocking = spi_byte_brightness_set_blocking;
+@@ -121,7 +118,11 @@ static int spi_byte_probe(struct spi_device *spi)
+       spi_byte_brightness_set_blocking(&led->ldev,
+                                        led->ldev.brightness);
+-      ret = devm_led_classdev_register(&spi->dev, &led->ldev);
++      init_data.fwnode = of_fwnode_handle(child);
++      init_data.devicename = "leds-spi-byte";
++      init_data.default_label = ":";
++
++      ret = devm_led_classdev_register_ext(&spi->dev, &led->ldev, &init_data);
+       if (ret) {
+               of_node_put(child);
+               mutex_destroy(&led->mutex);
+-- 
+2.51.0
+
diff --git a/queue-6.1/revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch b/queue-6.1/revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch
new file mode 100644 (file)
index 0000000..b3ad4e0
--- /dev/null
@@ -0,0 +1,176 @@
+From 1aac4f86b553d802d684e72687c45a8efe5ce6a7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 29 Jul 2025 17:11:53 -0400
+Subject: Revert "xfrm: destroy xfrm_state synchronously on net exit path"
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit 2a198bbec6913ae1c90ec963750003c6213668c7 ]
+
+This reverts commit f75a2804da391571563c4b6b29e7797787332673.
+
+With all states (whether user or kern) removed from the hashtables
+during deletion, there's no need for synchronous destruction of
+states. xfrm6_tunnel states still need to have been destroyed (which
+will be the case when its last user is deleted (not destroyed)) so
+that xfrm6_tunnel_free_spi removes it from the per-netns hashtable
+before the netns is destroyed.
+
+This has the benefit of skipping one synchronize_rcu per state (in
+__xfrm_state_destroy(sync=true)) when we exit a netns.
+
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/xfrm.h      | 12 +++---------
+ net/ipv6/xfrm6_tunnel.c |  2 +-
+ net/key/af_key.c        |  2 +-
+ net/xfrm/xfrm_state.c   | 23 +++++++++--------------
+ net/xfrm/xfrm_user.c    |  2 +-
+ 5 files changed, 15 insertions(+), 26 deletions(-)
+
+diff --git a/include/net/xfrm.h b/include/net/xfrm.h
+index f639ede8de758..616ce2cb73f37 100644
+--- a/include/net/xfrm.h
++++ b/include/net/xfrm.h
+@@ -791,7 +791,7 @@ static inline void xfrm_pols_put(struct xfrm_policy **pols, int npols)
+               xfrm_pol_put(pols[i]);
+ }
+-void __xfrm_state_destroy(struct xfrm_state *, bool);
++void __xfrm_state_destroy(struct xfrm_state *);
+ static inline void __xfrm_state_put(struct xfrm_state *x)
+ {
+@@ -801,13 +801,7 @@ static inline void __xfrm_state_put(struct xfrm_state *x)
+ static inline void xfrm_state_put(struct xfrm_state *x)
+ {
+       if (refcount_dec_and_test(&x->refcnt))
+-              __xfrm_state_destroy(x, false);
+-}
+-
+-static inline void xfrm_state_put_sync(struct xfrm_state *x)
+-{
+-      if (refcount_dec_and_test(&x->refcnt))
+-              __xfrm_state_destroy(x, true);
++              __xfrm_state_destroy(x);
+ }
+ static inline void xfrm_state_hold(struct xfrm_state *x)
+@@ -1584,7 +1578,7 @@ struct xfrmk_spdinfo {
+ struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq);
+ int xfrm_state_delete(struct xfrm_state *x);
+-int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync);
++int xfrm_state_flush(struct net *net, u8 proto, bool task_valid);
+ int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid);
+ void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
+ void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
+diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
+index 1702b4de1c1ef..3645ab427e128 100644
+--- a/net/ipv6/xfrm6_tunnel.c
++++ b/net/ipv6/xfrm6_tunnel.c
+@@ -334,7 +334,7 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
+       struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
+       unsigned int i;
+-      xfrm_state_flush(net, 0, false, true);
++      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
+       xfrm_flush_gc();
+       for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
+diff --git a/net/key/af_key.c b/net/key/af_key.c
+index 8a8f2429d5d99..0fcd348c249fb 100644
+--- a/net/key/af_key.c
++++ b/net/key/af_key.c
+@@ -1766,7 +1766,7 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_m
+       if (proto == 0)
+               return -EINVAL;
+-      err = xfrm_state_flush(net, proto, true, false);
++      err = xfrm_state_flush(net, proto, true);
+       err2 = unicast_flush_resp(sk, hdr);
+       if (err || err2) {
+               if (err == -ESRCH) /* empty table - go quietly */
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index 42c7224d763f2..f45b4611b1b3b 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -498,7 +498,7 @@ void xfrm_state_free(struct xfrm_state *x)
+ }
+ EXPORT_SYMBOL(xfrm_state_free);
+-static void ___xfrm_state_destroy(struct xfrm_state *x)
++static void xfrm_state_gc_destroy(struct xfrm_state *x)
+ {
+       hrtimer_cancel(&x->mtimer);
+       del_timer_sync(&x->rtimer);
+@@ -536,7 +536,7 @@ static void xfrm_state_gc_task(struct work_struct *work)
+       synchronize_rcu();
+       hlist_for_each_entry_safe(x, tmp, &gc_list, gclist)
+-              ___xfrm_state_destroy(x);
++              xfrm_state_gc_destroy(x);
+ }
+ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
+@@ -660,19 +660,14 @@ struct xfrm_state *xfrm_state_alloc(struct net *net)
+ }
+ EXPORT_SYMBOL(xfrm_state_alloc);
+-void __xfrm_state_destroy(struct xfrm_state *x, bool sync)
++void __xfrm_state_destroy(struct xfrm_state *x)
+ {
+       WARN_ON(x->km.state != XFRM_STATE_DEAD);
+-      if (sync) {
+-              synchronize_rcu();
+-              ___xfrm_state_destroy(x);
+-      } else {
+-              spin_lock_bh(&xfrm_state_gc_lock);
+-              hlist_add_head(&x->gclist, &xfrm_state_gc_list);
+-              spin_unlock_bh(&xfrm_state_gc_lock);
+-              schedule_work(&xfrm_state_gc_work);
+-      }
++      spin_lock_bh(&xfrm_state_gc_lock);
++      hlist_add_head(&x->gclist, &xfrm_state_gc_list);
++      spin_unlock_bh(&xfrm_state_gc_lock);
++      schedule_work(&xfrm_state_gc_work);
+ }
+ EXPORT_SYMBOL(__xfrm_state_destroy);
+@@ -780,7 +775,7 @@ xfrm_dev_state_flush_secctx_check(struct net *net, struct net_device *dev, bool
+ }
+ #endif
+-int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync)
++int xfrm_state_flush(struct net *net, u8 proto, bool task_valid)
+ {
+       int i, err = 0, cnt = 0;
+@@ -2751,7 +2746,7 @@ void xfrm_state_fini(struct net *net)
+       unsigned int sz;
+       flush_work(&net->xfrm.state_hash_work);
+-      xfrm_state_flush(net, 0, false, true);
++      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
+       flush_work(&xfrm_state_gc_work);
+       WARN_ON(!list_empty(&net->xfrm.state_all));
+diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
+index 2f6e0513dee51..e4f9b98a46d9c 100644
+--- a/net/xfrm/xfrm_user.c
++++ b/net/xfrm/xfrm_user.c
+@@ -2340,7 +2340,7 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
+       struct xfrm_usersa_flush *p = nlmsg_data(nlh);
+       int err;
+-      err = xfrm_state_flush(net, p->proto, true, false);
++      err = xfrm_state_flush(net, p->proto, true);
+       if (err) {
+               if (err == -ESRCH) /* empty table */
+                       return 0;
+-- 
+2.51.0
+
diff --git a/queue-6.1/series b/queue-6.1/series
new file mode 100644 (file)
index 0000000..b3a31a1
--- /dev/null
@@ -0,0 +1,6 @@
+xfrm-delete-x-tunnel-as-we-delete-x.patch
+revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch
+xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch
+xfrm-flush-all-states-in-xfrm_state_fini.patch
+leds-replace-all-non-returning-strlcpy-with-strscpy.patch
+leds-spi-byte-use-devm_led_classdev_register_ext.patch
diff --git a/queue-6.1/xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch b/queue-6.1/xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch
new file mode 100644 (file)
index 0000000..637cf16
--- /dev/null
@@ -0,0 +1,76 @@
+From 010ff2c8a17ffeac8b290d5aac03e9b9362a817e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 4 Dec 2025 17:43:27 -0500
+Subject: xfrm: also call xfrm_state_delete_tunnel at destroy time for states
+ that were never added
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit 10deb69864840ccf96b00ac2ab3a2055c0c04721 ]
+
+In commit b441cf3f8c4b ("xfrm: delete x->tunnel as we delete x"), I
+missed the case where state creation fails between full
+initialization (->init_state has been called) and being inserted on
+the lists.
+
+In this situation, ->init_state has been called, so for IPcomp
+tunnels, the fallback tunnel has been created and added onto the
+lists, but the user state never gets added, because we fail before
+that. The user state doesn't go through __xfrm_state_delete, so we
+don't call xfrm_state_delete_tunnel for those states, and we end up
+leaking the FB tunnel.
+
+There are several codepaths affected by this: the add/update paths, in
+both net/key and xfrm, and the migrate code (xfrm_migrate,
+xfrm_state_migrate). A "proper" rollback of the init_state work would
+probably be doable in the add/update code, but for migrate it gets
+more complicated as multiple states may be involved.
+
+At some point, the new (not-inserted) state will be destroyed, so call
+xfrm_state_delete_tunnel during xfrm_state_gc_destroy. Most states
+will have their fallback tunnel cleaned up during __xfrm_state_delete,
+which solves the issue that b441cf3f8c4b (and other patches before it)
+aimed at. All states (including FB tunnels) will be removed from the
+lists once xfrm_state_fini has called flush_work(&xfrm_state_gc_work).
+
+Reported-by: syzbot+999eb23467f83f9bf9bf@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=999eb23467f83f9bf9bf
+Fixes: b441cf3f8c4b ("xfrm: delete x->tunnel as we delete x")
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/xfrm/xfrm_state.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index f45b4611b1b3b..78d9900db4387 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -498,6 +498,7 @@ void xfrm_state_free(struct xfrm_state *x)
+ }
+ EXPORT_SYMBOL(xfrm_state_free);
++static void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ static void xfrm_state_gc_destroy(struct xfrm_state *x)
+ {
+       hrtimer_cancel(&x->mtimer);
+@@ -512,6 +513,7 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
+       kfree(x->preplay_esn);
+       if (x->type_offload)
+               xfrm_put_type_offload(x->type_offload);
++      xfrm_state_delete_tunnel(x);
+       if (x->type) {
+               x->type->destructor(x);
+               xfrm_put_type(x->type);
+@@ -671,7 +673,6 @@ void __xfrm_state_destroy(struct xfrm_state *x)
+ }
+ EXPORT_SYMBOL(__xfrm_state_destroy);
+-static void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ int __xfrm_state_delete(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+-- 
+2.51.0
+
diff --git a/queue-6.1/xfrm-delete-x-tunnel-as-we-delete-x.patch b/queue-6.1/xfrm-delete-x-tunnel-as-we-delete-x.patch
new file mode 100644 (file)
index 0000000..a0da224
--- /dev/null
@@ -0,0 +1,196 @@
+From 05aa9a7c5628fcbdc6dfd99d3380ec9413c23015 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 29 Jul 2025 17:11:52 -0400
+Subject: xfrm: delete x->tunnel as we delete x
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit b441cf3f8c4b8576639d20c8eb4aa32917602ecd ]
+
+The ipcomp fallback tunnels currently get deleted (from the various
+lists and hashtables) as the last user state that needed that fallback
+is destroyed (not deleted). If a reference to that user state still
+exists, the fallback state will remain on the hashtables/lists,
+triggering the WARN in xfrm_state_fini. Because of those remaining
+references, the fix in commit f75a2804da39 ("xfrm: destroy xfrm_state
+synchronously on net exit path") is not complete.
+
+We recently fixed one such situation in TCP due to defered freeing of
+skbs (commit 9b6412e6979f ("tcp: drop secpath at the same time as we
+currently drop dst")). This can also happen due to IP reassembly: skbs
+with a secpath remain on the reassembly queue until netns
+destruction. If we can't guarantee that the queues are flushed by the
+time xfrm_state_fini runs, there may still be references to a (user)
+xfrm_state, preventing the timely deletion of the corresponding
+fallback state.
+
+Instead of chasing each instance of skbs holding a secpath one by one,
+this patch fixes the issue directly within xfrm, by deleting the
+fallback state as soon as the last user state depending on it has been
+deleted. Destruction will still happen when the final reference is
+dropped.
+
+A separate lockdep class for the fallback state is required since
+we're going to lock x->tunnel while x is locked.
+
+Fixes: 9d4139c76905 ("netns xfrm: per-netns xfrm_state_all list")
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/xfrm.h      |  1 -
+ net/ipv4/ipcomp.c       |  2 ++
+ net/ipv6/ipcomp6.c      |  2 ++
+ net/ipv6/xfrm6_tunnel.c |  2 +-
+ net/xfrm/xfrm_ipcomp.c  |  1 -
+ net/xfrm/xfrm_state.c   | 19 ++++++++-----------
+ 6 files changed, 13 insertions(+), 14 deletions(-)
+
+diff --git a/include/net/xfrm.h b/include/net/xfrm.h
+index ac5db167370c9..f639ede8de758 100644
+--- a/include/net/xfrm.h
++++ b/include/net/xfrm.h
+@@ -398,7 +398,6 @@ int xfrm_input_register_afinfo(const struct xfrm_input_afinfo *afinfo);
+ int xfrm_input_unregister_afinfo(const struct xfrm_input_afinfo *afinfo);
+ void xfrm_flush_gc(void);
+-void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ struct xfrm_type {
+       struct module           *owner;
+diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
+index 5a4fb2539b08b..9a45aed508d19 100644
+--- a/net/ipv4/ipcomp.c
++++ b/net/ipv4/ipcomp.c
+@@ -54,6 +54,7 @@ static int ipcomp4_err(struct sk_buff *skb, u32 info)
+ }
+ /* We always hold one tunnel user reference to indicate a tunnel */
++static struct lock_class_key xfrm_state_lock_key;
+ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+@@ -62,6 +63,7 @@ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
+       t = xfrm_state_alloc(net);
+       if (!t)
+               goto out;
++      lockdep_set_class(&t->lock, &xfrm_state_lock_key);
+       t->id.proto = IPPROTO_IPIP;
+       t->id.spi = x->props.saddr.a4;
+diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
+index 72d4858dec18a..8607569de34f3 100644
+--- a/net/ipv6/ipcomp6.c
++++ b/net/ipv6/ipcomp6.c
+@@ -71,6 +71,7 @@ static int ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+       return 0;
+ }
++static struct lock_class_key xfrm_state_lock_key;
+ static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+@@ -79,6 +80,7 @@ static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
+       t = xfrm_state_alloc(net);
+       if (!t)
+               goto out;
++      lockdep_set_class(&t->lock, &xfrm_state_lock_key);
+       t->id.proto = IPPROTO_IPV6;
+       t->id.spi = xfrm6_tunnel_alloc_spi(net, (xfrm_address_t *)&x->props.saddr);
+diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
+index 1323f2f6928e2..1702b4de1c1ef 100644
+--- a/net/ipv6/xfrm6_tunnel.c
++++ b/net/ipv6/xfrm6_tunnel.c
+@@ -334,8 +334,8 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
+       struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
+       unsigned int i;
+-      xfrm_flush_gc();
+       xfrm_state_flush(net, 0, false, true);
++      xfrm_flush_gc();
+       for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
+               WARN_ON_ONCE(!hlist_empty(&xfrm6_tn->spi_byaddr[i]));
+diff --git a/net/xfrm/xfrm_ipcomp.c b/net/xfrm/xfrm_ipcomp.c
+index 80143360bf095..7471f22a08ad2 100644
+--- a/net/xfrm/xfrm_ipcomp.c
++++ b/net/xfrm/xfrm_ipcomp.c
+@@ -318,7 +318,6 @@ void ipcomp_destroy(struct xfrm_state *x)
+       struct ipcomp_data *ipcd = x->data;
+       if (!ipcd)
+               return;
+-      xfrm_state_delete_tunnel(x);
+       mutex_lock(&ipcomp_resource_mutex);
+       ipcomp_free_data(ipcd);
+       mutex_unlock(&ipcomp_resource_mutex);
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index 58c53bb1c5838..42c7224d763f2 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -676,6 +676,7 @@ void __xfrm_state_destroy(struct xfrm_state *x, bool sync)
+ }
+ EXPORT_SYMBOL(__xfrm_state_destroy);
++static void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ int __xfrm_state_delete(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+@@ -696,6 +697,8 @@ int __xfrm_state_delete(struct xfrm_state *x)
+               xfrm_dev_state_delete(x);
++              xfrm_state_delete_tunnel(x);
++
+               /* All xfrm_state objects are created by xfrm_state_alloc.
+                * The xfrm_state_alloc call gives a reference, and that
+                * is what we are dropping here.
+@@ -799,10 +802,7 @@ int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync)
+                               err = xfrm_state_delete(x);
+                               xfrm_audit_state_delete(x, err ? 0 : 1,
+                                                       task_valid);
+-                              if (sync)
+-                                      xfrm_state_put_sync(x);
+-                              else
+-                                      xfrm_state_put(x);
++                              xfrm_state_put(x);
+                               if (!err)
+                                       cnt++;
+@@ -2563,20 +2563,17 @@ void xfrm_flush_gc(void)
+ }
+ EXPORT_SYMBOL(xfrm_flush_gc);
+-/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
+-void xfrm_state_delete_tunnel(struct xfrm_state *x)
++static void xfrm_state_delete_tunnel(struct xfrm_state *x)
+ {
+       if (x->tunnel) {
+               struct xfrm_state *t = x->tunnel;
+-              if (atomic_read(&t->tunnel_users) == 2)
++              if (atomic_dec_return(&t->tunnel_users) == 1)
+                       xfrm_state_delete(t);
+-              atomic_dec(&t->tunnel_users);
+-              xfrm_state_put_sync(t);
++              xfrm_state_put(t);
+               x->tunnel = NULL;
+       }
+ }
+-EXPORT_SYMBOL(xfrm_state_delete_tunnel);
+ u32 xfrm_state_mtu(struct xfrm_state *x, int mtu)
+ {
+@@ -2754,8 +2751,8 @@ void xfrm_state_fini(struct net *net)
+       unsigned int sz;
+       flush_work(&net->xfrm.state_hash_work);
+-      flush_work(&xfrm_state_gc_work);
+       xfrm_state_flush(net, 0, false, true);
++      flush_work(&xfrm_state_gc_work);
+       WARN_ON(!list_empty(&net->xfrm.state_all));
+-- 
+2.51.0
+
diff --git a/queue-6.1/xfrm-flush-all-states-in-xfrm_state_fini.patch b/queue-6.1/xfrm-flush-all-states-in-xfrm_state_fini.patch
new file mode 100644 (file)
index 0000000..f51a671
--- /dev/null
@@ -0,0 +1,61 @@
+From e29dc2969828774e9cb8ef6babdc8779997c8150 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 4 Aug 2025 11:05:43 +0200
+Subject: xfrm: flush all states in xfrm_state_fini
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit 42e42562c9cfcdacf000f1b42284a4fad24f8546 ]
+
+While reverting commit f75a2804da39 ("xfrm: destroy xfrm_state
+synchronously on net exit path"), I incorrectly changed
+xfrm_state_flush's "proto" argument back to IPSEC_PROTO_ANY. This
+reverts some of the changes in commit dbb2483b2a46 ("xfrm: clean up
+xfrm protocol checks"), and leads to some states not being removed
+when we exit the netns.
+
+Pass 0 instead of IPSEC_PROTO_ANY from both xfrm_state_fini
+xfrm6_tunnel_net_exit, so that xfrm_state_flush deletes all states.
+
+Fixes: 2a198bbec691 ("Revert "xfrm: destroy xfrm_state synchronously on net exit path"")
+Reported-by: syzbot+6641a61fe0e2e89ae8c5@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=6641a61fe0e2e89ae8c5
+Tested-by: syzbot+6641a61fe0e2e89ae8c5@syzkaller.appspotmail.com
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv6/xfrm6_tunnel.c | 2 +-
+ net/xfrm/xfrm_state.c   | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
+index 3645ab427e128..775ae1171ef89 100644
+--- a/net/ipv6/xfrm6_tunnel.c
++++ b/net/ipv6/xfrm6_tunnel.c
+@@ -334,7 +334,7 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
+       struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
+       unsigned int i;
+-      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
++      xfrm_state_flush(net, 0, false);
+       xfrm_flush_gc();
+       for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index 78d9900db4387..38d9b0b5cc5db 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -2747,7 +2747,7 @@ void xfrm_state_fini(struct net *net)
+       unsigned int sz;
+       flush_work(&net->xfrm.state_hash_work);
+-      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
++      xfrm_state_flush(net, 0, false);
+       flush_work(&xfrm_state_gc_work);
+       WARN_ON(!list_empty(&net->xfrm.state_all));
+-- 
+2.51.0
+
diff --git a/queue-6.12/revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch b/queue-6.12/revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch
new file mode 100644 (file)
index 0000000..341b605
--- /dev/null
@@ -0,0 +1,176 @@
+From 65be838ba6e7090620032e23ab6a9b3644ceb65a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 29 Jul 2025 17:11:53 -0400
+Subject: Revert "xfrm: destroy xfrm_state synchronously on net exit path"
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit 2a198bbec6913ae1c90ec963750003c6213668c7 ]
+
+This reverts commit f75a2804da391571563c4b6b29e7797787332673.
+
+With all states (whether user or kern) removed from the hashtables
+during deletion, there's no need for synchronous destruction of
+states. xfrm6_tunnel states still need to have been destroyed (which
+will be the case when its last user is deleted (not destroyed)) so
+that xfrm6_tunnel_free_spi removes it from the per-netns hashtable
+before the netns is destroyed.
+
+This has the benefit of skipping one synchronize_rcu per state (in
+__xfrm_state_destroy(sync=true)) when we exit a netns.
+
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/xfrm.h      | 12 +++---------
+ net/ipv6/xfrm6_tunnel.c |  2 +-
+ net/key/af_key.c        |  2 +-
+ net/xfrm/xfrm_state.c   | 23 +++++++++--------------
+ net/xfrm/xfrm_user.c    |  2 +-
+ 5 files changed, 15 insertions(+), 26 deletions(-)
+
+diff --git a/include/net/xfrm.h b/include/net/xfrm.h
+index d51204041bf7d..b6fff506bf30c 100644
+--- a/include/net/xfrm.h
++++ b/include/net/xfrm.h
+@@ -851,7 +851,7 @@ static inline void xfrm_pols_put(struct xfrm_policy **pols, int npols)
+               xfrm_pol_put(pols[i]);
+ }
+-void __xfrm_state_destroy(struct xfrm_state *, bool);
++void __xfrm_state_destroy(struct xfrm_state *);
+ static inline void __xfrm_state_put(struct xfrm_state *x)
+ {
+@@ -861,13 +861,7 @@ static inline void __xfrm_state_put(struct xfrm_state *x)
+ static inline void xfrm_state_put(struct xfrm_state *x)
+ {
+       if (refcount_dec_and_test(&x->refcnt))
+-              __xfrm_state_destroy(x, false);
+-}
+-
+-static inline void xfrm_state_put_sync(struct xfrm_state *x)
+-{
+-      if (refcount_dec_and_test(&x->refcnt))
+-              __xfrm_state_destroy(x, true);
++              __xfrm_state_destroy(x);
+ }
+ static inline void xfrm_state_hold(struct xfrm_state *x)
+@@ -1705,7 +1699,7 @@ struct xfrmk_spdinfo {
+ struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq, u32 pcpu_num);
+ int xfrm_state_delete(struct xfrm_state *x);
+-int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync);
++int xfrm_state_flush(struct net *net, u8 proto, bool task_valid);
+ int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid);
+ int xfrm_dev_policy_flush(struct net *net, struct net_device *dev,
+                         bool task_valid);
+diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
+index 7fd8bc08e6eb1..5120a763da0d9 100644
+--- a/net/ipv6/xfrm6_tunnel.c
++++ b/net/ipv6/xfrm6_tunnel.c
+@@ -334,7 +334,7 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
+       struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
+       unsigned int i;
+-      xfrm_state_flush(net, 0, false, true);
++      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
+       xfrm_flush_gc();
+       for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
+diff --git a/net/key/af_key.c b/net/key/af_key.c
+index c56bb4f451e6d..9dea2b26e5069 100644
+--- a/net/key/af_key.c
++++ b/net/key/af_key.c
+@@ -1766,7 +1766,7 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_m
+       if (proto == 0)
+               return -EINVAL;
+-      err = xfrm_state_flush(net, proto, true, false);
++      err = xfrm_state_flush(net, proto, true);
+       err2 = unicast_flush_resp(sk, hdr);
+       if (err || err2) {
+               if (err == -ESRCH) /* empty table - go quietly */
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index e4500d481e26b..9cd747cfcc34c 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -531,7 +531,7 @@ void xfrm_state_free(struct xfrm_state *x)
+ }
+ EXPORT_SYMBOL(xfrm_state_free);
+-static void ___xfrm_state_destroy(struct xfrm_state *x)
++static void xfrm_state_gc_destroy(struct xfrm_state *x)
+ {
+       hrtimer_cancel(&x->mtimer);
+       del_timer_sync(&x->rtimer);
+@@ -569,7 +569,7 @@ static void xfrm_state_gc_task(struct work_struct *work)
+       synchronize_rcu();
+       hlist_for_each_entry_safe(x, tmp, &gc_list, gclist)
+-              ___xfrm_state_destroy(x);
++              xfrm_state_gc_destroy(x);
+ }
+ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
+@@ -732,19 +732,14 @@ void xfrm_dev_state_free(struct xfrm_state *x)
+ }
+ #endif
+-void __xfrm_state_destroy(struct xfrm_state *x, bool sync)
++void __xfrm_state_destroy(struct xfrm_state *x)
+ {
+       WARN_ON(x->km.state != XFRM_STATE_DEAD);
+-      if (sync) {
+-              synchronize_rcu();
+-              ___xfrm_state_destroy(x);
+-      } else {
+-              spin_lock_bh(&xfrm_state_gc_lock);
+-              hlist_add_head(&x->gclist, &xfrm_state_gc_list);
+-              spin_unlock_bh(&xfrm_state_gc_lock);
+-              schedule_work(&xfrm_state_gc_work);
+-      }
++      spin_lock_bh(&xfrm_state_gc_lock);
++      hlist_add_head(&x->gclist, &xfrm_state_gc_list);
++      spin_unlock_bh(&xfrm_state_gc_lock);
++      schedule_work(&xfrm_state_gc_work);
+ }
+ EXPORT_SYMBOL(__xfrm_state_destroy);
+@@ -859,7 +854,7 @@ xfrm_dev_state_flush_secctx_check(struct net *net, struct net_device *dev, bool
+ }
+ #endif
+-int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync)
++int xfrm_state_flush(struct net *net, u8 proto, bool task_valid)
+ {
+       int i, err = 0, cnt = 0;
+@@ -3218,7 +3213,7 @@ void xfrm_state_fini(struct net *net)
+       unsigned int sz;
+       flush_work(&net->xfrm.state_hash_work);
+-      xfrm_state_flush(net, 0, false, true);
++      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
+       flush_work(&xfrm_state_gc_work);
+       WARN_ON(!list_empty(&net->xfrm.state_all));
+diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
+index 3d0fdeebaf3c8..1a4d2fac08594 100644
+--- a/net/xfrm/xfrm_user.c
++++ b/net/xfrm/xfrm_user.c
+@@ -2571,7 +2571,7 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
+       struct xfrm_usersa_flush *p = nlmsg_data(nlh);
+       int err;
+-      err = xfrm_state_flush(net, p->proto, true, false);
++      err = xfrm_state_flush(net, p->proto, true);
+       if (err) {
+               if (err == -ESRCH) /* empty table */
+                       return 0;
+-- 
+2.51.0
+
diff --git a/queue-6.12/series b/queue-6.12/series
new file mode 100644 (file)
index 0000000..7c20dd9
--- /dev/null
@@ -0,0 +1,4 @@
+xfrm-delete-x-tunnel-as-we-delete-x.patch
+revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch
+xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch
+xfrm-flush-all-states-in-xfrm_state_fini.patch
diff --git a/queue-6.12/xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch b/queue-6.12/xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch
new file mode 100644 (file)
index 0000000..f351009
--- /dev/null
@@ -0,0 +1,76 @@
+From 8532ed86015e7e19903c02280e7ee221596f9872 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 4 Dec 2025 17:43:27 -0500
+Subject: xfrm: also call xfrm_state_delete_tunnel at destroy time for states
+ that were never added
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit 10deb69864840ccf96b00ac2ab3a2055c0c04721 ]
+
+In commit b441cf3f8c4b ("xfrm: delete x->tunnel as we delete x"), I
+missed the case where state creation fails between full
+initialization (->init_state has been called) and being inserted on
+the lists.
+
+In this situation, ->init_state has been called, so for IPcomp
+tunnels, the fallback tunnel has been created and added onto the
+lists, but the user state never gets added, because we fail before
+that. The user state doesn't go through __xfrm_state_delete, so we
+don't call xfrm_state_delete_tunnel for those states, and we end up
+leaking the FB tunnel.
+
+There are several codepaths affected by this: the add/update paths, in
+both net/key and xfrm, and the migrate code (xfrm_migrate,
+xfrm_state_migrate). A "proper" rollback of the init_state work would
+probably be doable in the add/update code, but for migrate it gets
+more complicated as multiple states may be involved.
+
+At some point, the new (not-inserted) state will be destroyed, so call
+xfrm_state_delete_tunnel during xfrm_state_gc_destroy. Most states
+will have their fallback tunnel cleaned up during __xfrm_state_delete,
+which solves the issue that b441cf3f8c4b (and other patches before it)
+aimed at. All states (including FB tunnels) will be removed from the
+lists once xfrm_state_fini has called flush_work(&xfrm_state_gc_work).
+
+Reported-by: syzbot+999eb23467f83f9bf9bf@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=999eb23467f83f9bf9bf
+Fixes: b441cf3f8c4b ("xfrm: delete x->tunnel as we delete x")
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/xfrm/xfrm_state.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index 9cd747cfcc34c..3a79ebcbbc369 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -531,6 +531,7 @@ void xfrm_state_free(struct xfrm_state *x)
+ }
+ EXPORT_SYMBOL(xfrm_state_free);
++static void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ static void xfrm_state_gc_destroy(struct xfrm_state *x)
+ {
+       hrtimer_cancel(&x->mtimer);
+@@ -545,6 +546,7 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
+       kfree(x->preplay_esn);
+       if (x->type_offload)
+               xfrm_put_type_offload(x->type_offload);
++      xfrm_state_delete_tunnel(x);
+       if (x->type) {
+               x->type->destructor(x);
+               xfrm_put_type(x->type);
+@@ -743,7 +745,6 @@ void __xfrm_state_destroy(struct xfrm_state *x)
+ }
+ EXPORT_SYMBOL(__xfrm_state_destroy);
+-static void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ int __xfrm_state_delete(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+-- 
+2.51.0
+
diff --git a/queue-6.12/xfrm-delete-x-tunnel-as-we-delete-x.patch b/queue-6.12/xfrm-delete-x-tunnel-as-we-delete-x.patch
new file mode 100644 (file)
index 0000000..201ab3d
--- /dev/null
@@ -0,0 +1,196 @@
+From bc57480bdba5cd254b0265acb19de9154c14900f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 29 Jul 2025 17:11:52 -0400
+Subject: xfrm: delete x->tunnel as we delete x
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit b441cf3f8c4b8576639d20c8eb4aa32917602ecd ]
+
+The ipcomp fallback tunnels currently get deleted (from the various
+lists and hashtables) as the last user state that needed that fallback
+is destroyed (not deleted). If a reference to that user state still
+exists, the fallback state will remain on the hashtables/lists,
+triggering the WARN in xfrm_state_fini. Because of those remaining
+references, the fix in commit f75a2804da39 ("xfrm: destroy xfrm_state
+synchronously on net exit path") is not complete.
+
+We recently fixed one such situation in TCP due to defered freeing of
+skbs (commit 9b6412e6979f ("tcp: drop secpath at the same time as we
+currently drop dst")). This can also happen due to IP reassembly: skbs
+with a secpath remain on the reassembly queue until netns
+destruction. If we can't guarantee that the queues are flushed by the
+time xfrm_state_fini runs, there may still be references to a (user)
+xfrm_state, preventing the timely deletion of the corresponding
+fallback state.
+
+Instead of chasing each instance of skbs holding a secpath one by one,
+this patch fixes the issue directly within xfrm, by deleting the
+fallback state as soon as the last user state depending on it has been
+deleted. Destruction will still happen when the final reference is
+dropped.
+
+A separate lockdep class for the fallback state is required since
+we're going to lock x->tunnel while x is locked.
+
+Fixes: 9d4139c76905 ("netns xfrm: per-netns xfrm_state_all list")
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/xfrm.h      |  1 -
+ net/ipv4/ipcomp.c       |  2 ++
+ net/ipv6/ipcomp6.c      |  2 ++
+ net/ipv6/xfrm6_tunnel.c |  2 +-
+ net/xfrm/xfrm_ipcomp.c  |  1 -
+ net/xfrm/xfrm_state.c   | 19 ++++++++-----------
+ 6 files changed, 13 insertions(+), 14 deletions(-)
+
+diff --git a/include/net/xfrm.h b/include/net/xfrm.h
+index caaff61601a07..d51204041bf7d 100644
+--- a/include/net/xfrm.h
++++ b/include/net/xfrm.h
+@@ -424,7 +424,6 @@ int xfrm_input_register_afinfo(const struct xfrm_input_afinfo *afinfo);
+ int xfrm_input_unregister_afinfo(const struct xfrm_input_afinfo *afinfo);
+ void xfrm_flush_gc(void);
+-void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ struct xfrm_type {
+       struct module           *owner;
+diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
+index 5a4fb2539b08b..9a45aed508d19 100644
+--- a/net/ipv4/ipcomp.c
++++ b/net/ipv4/ipcomp.c
+@@ -54,6 +54,7 @@ static int ipcomp4_err(struct sk_buff *skb, u32 info)
+ }
+ /* We always hold one tunnel user reference to indicate a tunnel */
++static struct lock_class_key xfrm_state_lock_key;
+ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+@@ -62,6 +63,7 @@ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
+       t = xfrm_state_alloc(net);
+       if (!t)
+               goto out;
++      lockdep_set_class(&t->lock, &xfrm_state_lock_key);
+       t->id.proto = IPPROTO_IPIP;
+       t->id.spi = x->props.saddr.a4;
+diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
+index 72d4858dec18a..8607569de34f3 100644
+--- a/net/ipv6/ipcomp6.c
++++ b/net/ipv6/ipcomp6.c
+@@ -71,6 +71,7 @@ static int ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+       return 0;
+ }
++static struct lock_class_key xfrm_state_lock_key;
+ static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+@@ -79,6 +80,7 @@ static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
+       t = xfrm_state_alloc(net);
+       if (!t)
+               goto out;
++      lockdep_set_class(&t->lock, &xfrm_state_lock_key);
+       t->id.proto = IPPROTO_IPV6;
+       t->id.spi = xfrm6_tunnel_alloc_spi(net, (xfrm_address_t *)&x->props.saddr);
+diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
+index bf140ef781c1f..7fd8bc08e6eb1 100644
+--- a/net/ipv6/xfrm6_tunnel.c
++++ b/net/ipv6/xfrm6_tunnel.c
+@@ -334,8 +334,8 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
+       struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
+       unsigned int i;
+-      xfrm_flush_gc();
+       xfrm_state_flush(net, 0, false, true);
++      xfrm_flush_gc();
+       for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
+               WARN_ON_ONCE(!hlist_empty(&xfrm6_tn->spi_byaddr[i]));
+diff --git a/net/xfrm/xfrm_ipcomp.c b/net/xfrm/xfrm_ipcomp.c
+index 9c0fa0e1786a2..f2e70e918f114 100644
+--- a/net/xfrm/xfrm_ipcomp.c
++++ b/net/xfrm/xfrm_ipcomp.c
+@@ -315,7 +315,6 @@ void ipcomp_destroy(struct xfrm_state *x)
+       struct ipcomp_data *ipcd = x->data;
+       if (!ipcd)
+               return;
+-      xfrm_state_delete_tunnel(x);
+       mutex_lock(&ipcomp_resource_mutex);
+       ipcomp_free_data(ipcd);
+       mutex_unlock(&ipcomp_resource_mutex);
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index f8cb033f102ed..e4500d481e26b 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -748,6 +748,7 @@ void __xfrm_state_destroy(struct xfrm_state *x, bool sync)
+ }
+ EXPORT_SYMBOL(__xfrm_state_destroy);
++static void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ int __xfrm_state_delete(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+@@ -775,6 +776,8 @@ int __xfrm_state_delete(struct xfrm_state *x)
+               xfrm_dev_state_delete(x);
++              xfrm_state_delete_tunnel(x);
++
+               /* All xfrm_state objects are created by xfrm_state_alloc.
+                * The xfrm_state_alloc call gives a reference, and that
+                * is what we are dropping here.
+@@ -878,10 +881,7 @@ int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync)
+                               err = xfrm_state_delete(x);
+                               xfrm_audit_state_delete(x, err ? 0 : 1,
+                                                       task_valid);
+-                              if (sync)
+-                                      xfrm_state_put_sync(x);
+-                              else
+-                                      xfrm_state_put(x);
++                              xfrm_state_put(x);
+                               if (!err)
+                                       cnt++;
+@@ -3008,20 +3008,17 @@ void xfrm_flush_gc(void)
+ }
+ EXPORT_SYMBOL(xfrm_flush_gc);
+-/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
+-void xfrm_state_delete_tunnel(struct xfrm_state *x)
++static void xfrm_state_delete_tunnel(struct xfrm_state *x)
+ {
+       if (x->tunnel) {
+               struct xfrm_state *t = x->tunnel;
+-              if (atomic_read(&t->tunnel_users) == 2)
++              if (atomic_dec_return(&t->tunnel_users) == 1)
+                       xfrm_state_delete(t);
+-              atomic_dec(&t->tunnel_users);
+-              xfrm_state_put_sync(t);
++              xfrm_state_put(t);
+               x->tunnel = NULL;
+       }
+ }
+-EXPORT_SYMBOL(xfrm_state_delete_tunnel);
+ u32 xfrm_state_mtu(struct xfrm_state *x, int mtu)
+ {
+@@ -3221,8 +3218,8 @@ void xfrm_state_fini(struct net *net)
+       unsigned int sz;
+       flush_work(&net->xfrm.state_hash_work);
+-      flush_work(&xfrm_state_gc_work);
+       xfrm_state_flush(net, 0, false, true);
++      flush_work(&xfrm_state_gc_work);
+       WARN_ON(!list_empty(&net->xfrm.state_all));
+-- 
+2.51.0
+
diff --git a/queue-6.12/xfrm-flush-all-states-in-xfrm_state_fini.patch b/queue-6.12/xfrm-flush-all-states-in-xfrm_state_fini.patch
new file mode 100644 (file)
index 0000000..b9efb51
--- /dev/null
@@ -0,0 +1,61 @@
+From c6def40581e00b170db9654e9601ad3904cff26a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 4 Aug 2025 11:05:43 +0200
+Subject: xfrm: flush all states in xfrm_state_fini
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit 42e42562c9cfcdacf000f1b42284a4fad24f8546 ]
+
+While reverting commit f75a2804da39 ("xfrm: destroy xfrm_state
+synchronously on net exit path"), I incorrectly changed
+xfrm_state_flush's "proto" argument back to IPSEC_PROTO_ANY. This
+reverts some of the changes in commit dbb2483b2a46 ("xfrm: clean up
+xfrm protocol checks"), and leads to some states not being removed
+when we exit the netns.
+
+Pass 0 instead of IPSEC_PROTO_ANY from both xfrm_state_fini
+xfrm6_tunnel_net_exit, so that xfrm_state_flush deletes all states.
+
+Fixes: 2a198bbec691 ("Revert "xfrm: destroy xfrm_state synchronously on net exit path"")
+Reported-by: syzbot+6641a61fe0e2e89ae8c5@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=6641a61fe0e2e89ae8c5
+Tested-by: syzbot+6641a61fe0e2e89ae8c5@syzkaller.appspotmail.com
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv6/xfrm6_tunnel.c | 2 +-
+ net/xfrm/xfrm_state.c   | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
+index 5120a763da0d9..0a0eeaed05910 100644
+--- a/net/ipv6/xfrm6_tunnel.c
++++ b/net/ipv6/xfrm6_tunnel.c
+@@ -334,7 +334,7 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
+       struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
+       unsigned int i;
+-      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
++      xfrm_state_flush(net, 0, false);
+       xfrm_flush_gc();
+       for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index 3a79ebcbbc369..b9bac68364527 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -3214,7 +3214,7 @@ void xfrm_state_fini(struct net *net)
+       unsigned int sz;
+       flush_work(&net->xfrm.state_hash_work);
+-      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
++      xfrm_state_flush(net, 0, false);
+       flush_work(&xfrm_state_gc_work);
+       WARN_ON(!list_empty(&net->xfrm.state_all));
+-- 
+2.51.0
+
diff --git a/queue-6.6/leds-spi-byte-use-devm_led_classdev_register_ext.patch b/queue-6.6/leds-spi-byte-use-devm_led_classdev_register_ext.patch
new file mode 100644 (file)
index 0000000..1b7d325
--- /dev/null
@@ -0,0 +1,63 @@
+From 8e67f40cad245534b390ba73d65699a333c92beb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 4 Feb 2024 16:07:26 +0100
+Subject: leds: spi-byte: Use devm_led_classdev_register_ext()
+
+From: Stefan Kalscheuer <stefan@stklcode.de>
+
+[ Upstream commit ccc35ff2fd2911986b716a87fe65e03fac2312c9 ]
+
+Use extended classdev registration to generate generic device names from
+color and function enums instead of reading only the label from the
+device tree.
+
+Signed-off-by: Stefan Kalscheuer <stefan@stklcode.de>
+Link: https://lore.kernel.org/r/20240204150726.29783-1-stefan@stklcode.de
+Signed-off-by: Lee Jones <lee@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/leds/leds-spi-byte.c | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/leds/leds-spi-byte.c b/drivers/leds/leds-spi-byte.c
+index afe9bff7c7c16..b04cf502e6035 100644
+--- a/drivers/leds/leds-spi-byte.c
++++ b/drivers/leds/leds-spi-byte.c
+@@ -83,7 +83,7 @@ static int spi_byte_probe(struct spi_device *spi)
+       struct device_node *child;
+       struct device *dev = &spi->dev;
+       struct spi_byte_led *led;
+-      const char *name = "leds-spi-byte::";
++      struct led_init_data init_data = {};
+       const char *state;
+       int ret;
+@@ -96,12 +96,9 @@ static int spi_byte_probe(struct spi_device *spi)
+       if (!led)
+               return -ENOMEM;
+-      of_property_read_string(child, "label", &name);
+-      strscpy(led->name, name, sizeof(led->name));
+       led->spi = spi;
+       mutex_init(&led->mutex);
+       led->cdef = device_get_match_data(dev);
+-      led->ldev.name = led->name;
+       led->ldev.brightness = LED_OFF;
+       led->ldev.max_brightness = led->cdef->max_value - led->cdef->off_value;
+       led->ldev.brightness_set_blocking = spi_byte_brightness_set_blocking;
+@@ -121,7 +118,11 @@ static int spi_byte_probe(struct spi_device *spi)
+       spi_byte_brightness_set_blocking(&led->ldev,
+                                        led->ldev.brightness);
+-      ret = devm_led_classdev_register(&spi->dev, &led->ldev);
++      init_data.fwnode = of_fwnode_handle(child);
++      init_data.devicename = "leds-spi-byte";
++      init_data.default_label = ":";
++
++      ret = devm_led_classdev_register_ext(&spi->dev, &led->ldev, &init_data);
+       if (ret) {
+               of_node_put(child);
+               mutex_destroy(&led->mutex);
+-- 
+2.51.0
+
diff --git a/queue-6.6/revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch b/queue-6.6/revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch
new file mode 100644 (file)
index 0000000..6898a97
--- /dev/null
@@ -0,0 +1,176 @@
+From c1e3b7f59b2e975604ee858c3e3246652cd3a97b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 29 Jul 2025 17:11:53 -0400
+Subject: Revert "xfrm: destroy xfrm_state synchronously on net exit path"
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit 2a198bbec6913ae1c90ec963750003c6213668c7 ]
+
+This reverts commit f75a2804da391571563c4b6b29e7797787332673.
+
+With all states (whether user or kern) removed from the hashtables
+during deletion, there's no need for synchronous destruction of
+states. xfrm6_tunnel states still need to have been destroyed (which
+will be the case when its last user is deleted (not destroyed)) so
+that xfrm6_tunnel_free_spi removes it from the per-netns hashtable
+before the netns is destroyed.
+
+This has the benefit of skipping one synchronize_rcu per state (in
+__xfrm_state_destroy(sync=true)) when we exit a netns.
+
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/xfrm.h      | 12 +++---------
+ net/ipv6/xfrm6_tunnel.c |  2 +-
+ net/key/af_key.c        |  2 +-
+ net/xfrm/xfrm_state.c   | 23 +++++++++--------------
+ net/xfrm/xfrm_user.c    |  2 +-
+ 5 files changed, 15 insertions(+), 26 deletions(-)
+
+diff --git a/include/net/xfrm.h b/include/net/xfrm.h
+index f7a52b9b56acc..892fb8c31c322 100644
+--- a/include/net/xfrm.h
++++ b/include/net/xfrm.h
+@@ -809,7 +809,7 @@ static inline void xfrm_pols_put(struct xfrm_policy **pols, int npols)
+               xfrm_pol_put(pols[i]);
+ }
+-void __xfrm_state_destroy(struct xfrm_state *, bool);
++void __xfrm_state_destroy(struct xfrm_state *);
+ static inline void __xfrm_state_put(struct xfrm_state *x)
+ {
+@@ -819,13 +819,7 @@ static inline void __xfrm_state_put(struct xfrm_state *x)
+ static inline void xfrm_state_put(struct xfrm_state *x)
+ {
+       if (refcount_dec_and_test(&x->refcnt))
+-              __xfrm_state_destroy(x, false);
+-}
+-
+-static inline void xfrm_state_put_sync(struct xfrm_state *x)
+-{
+-      if (refcount_dec_and_test(&x->refcnt))
+-              __xfrm_state_destroy(x, true);
++              __xfrm_state_destroy(x);
+ }
+ static inline void xfrm_state_hold(struct xfrm_state *x)
+@@ -1661,7 +1655,7 @@ struct xfrmk_spdinfo {
+ struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq);
+ int xfrm_state_delete(struct xfrm_state *x);
+-int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync);
++int xfrm_state_flush(struct net *net, u8 proto, bool task_valid);
+ int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid);
+ int xfrm_dev_policy_flush(struct net *net, struct net_device *dev,
+                         bool task_valid);
+diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
+index 1702b4de1c1ef..3645ab427e128 100644
+--- a/net/ipv6/xfrm6_tunnel.c
++++ b/net/ipv6/xfrm6_tunnel.c
+@@ -334,7 +334,7 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
+       struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
+       unsigned int i;
+-      xfrm_state_flush(net, 0, false, true);
++      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
+       xfrm_flush_gc();
+       for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
+diff --git a/net/key/af_key.c b/net/key/af_key.c
+index d68d01804dc7b..6c4448908afed 100644
+--- a/net/key/af_key.c
++++ b/net/key/af_key.c
+@@ -1765,7 +1765,7 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_m
+       if (proto == 0)
+               return -EINVAL;
+-      err = xfrm_state_flush(net, proto, true, false);
++      err = xfrm_state_flush(net, proto, true);
+       err2 = unicast_flush_resp(sk, hdr);
+       if (err || err2) {
+               if (err == -ESRCH) /* empty table - go quietly */
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index 7c8aeec4fe274..b0e2958342ad2 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -521,7 +521,7 @@ void xfrm_state_free(struct xfrm_state *x)
+ }
+ EXPORT_SYMBOL(xfrm_state_free);
+-static void ___xfrm_state_destroy(struct xfrm_state *x)
++static void xfrm_state_gc_destroy(struct xfrm_state *x)
+ {
+       hrtimer_cancel(&x->mtimer);
+       del_timer_sync(&x->rtimer);
+@@ -559,7 +559,7 @@ static void xfrm_state_gc_task(struct work_struct *work)
+       synchronize_rcu();
+       hlist_for_each_entry_safe(x, tmp, &gc_list, gclist)
+-              ___xfrm_state_destroy(x);
++              xfrm_state_gc_destroy(x);
+ }
+ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
+@@ -720,19 +720,14 @@ void xfrm_dev_state_free(struct xfrm_state *x)
+ }
+ #endif
+-void __xfrm_state_destroy(struct xfrm_state *x, bool sync)
++void __xfrm_state_destroy(struct xfrm_state *x)
+ {
+       WARN_ON(x->km.state != XFRM_STATE_DEAD);
+-      if (sync) {
+-              synchronize_rcu();
+-              ___xfrm_state_destroy(x);
+-      } else {
+-              spin_lock_bh(&xfrm_state_gc_lock);
+-              hlist_add_head(&x->gclist, &xfrm_state_gc_list);
+-              spin_unlock_bh(&xfrm_state_gc_lock);
+-              schedule_work(&xfrm_state_gc_work);
+-      }
++      spin_lock_bh(&xfrm_state_gc_lock);
++      hlist_add_head(&x->gclist, &xfrm_state_gc_list);
++      spin_unlock_bh(&xfrm_state_gc_lock);
++      schedule_work(&xfrm_state_gc_work);
+ }
+ EXPORT_SYMBOL(__xfrm_state_destroy);
+@@ -840,7 +835,7 @@ xfrm_dev_state_flush_secctx_check(struct net *net, struct net_device *dev, bool
+ }
+ #endif
+-int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync)
++int xfrm_state_flush(struct net *net, u8 proto, bool task_valid)
+ {
+       int i, err = 0, cnt = 0;
+@@ -2986,7 +2981,7 @@ void xfrm_state_fini(struct net *net)
+       unsigned int sz;
+       flush_work(&net->xfrm.state_hash_work);
+-      xfrm_state_flush(net, 0, false, true);
++      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
+       flush_work(&xfrm_state_gc_work);
+       WARN_ON(!list_empty(&net->xfrm.state_all));
+diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
+index 1d91b42e79971..5b3ab4532aec2 100644
+--- a/net/xfrm/xfrm_user.c
++++ b/net/xfrm/xfrm_user.c
+@@ -2380,7 +2380,7 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
+       struct xfrm_usersa_flush *p = nlmsg_data(nlh);
+       int err;
+-      err = xfrm_state_flush(net, p->proto, true, false);
++      err = xfrm_state_flush(net, p->proto, true);
+       if (err) {
+               if (err == -ESRCH) /* empty table */
+                       return 0;
+-- 
+2.51.0
+
diff --git a/queue-6.6/series b/queue-6.6/series
new file mode 100644 (file)
index 0000000..244f90a
--- /dev/null
@@ -0,0 +1,5 @@
+xfrm-delete-x-tunnel-as-we-delete-x.patch
+revert-xfrm-destroy-xfrm_state-synchronously-on-net-.patch
+xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch
+xfrm-flush-all-states-in-xfrm_state_fini.patch
+leds-spi-byte-use-devm_led_classdev_register_ext.patch
diff --git a/queue-6.6/xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch b/queue-6.6/xfrm-also-call-xfrm_state_delete_tunnel-at-destroy-t.patch
new file mode 100644 (file)
index 0000000..2806be2
--- /dev/null
@@ -0,0 +1,76 @@
+From f0dc59723473bc993abcbe9b79eb61be0b05aa9d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 4 Dec 2025 17:43:27 -0500
+Subject: xfrm: also call xfrm_state_delete_tunnel at destroy time for states
+ that were never added
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit 10deb69864840ccf96b00ac2ab3a2055c0c04721 ]
+
+In commit b441cf3f8c4b ("xfrm: delete x->tunnel as we delete x"), I
+missed the case where state creation fails between full
+initialization (->init_state has been called) and being inserted on
+the lists.
+
+In this situation, ->init_state has been called, so for IPcomp
+tunnels, the fallback tunnel has been created and added onto the
+lists, but the user state never gets added, because we fail before
+that. The user state doesn't go through __xfrm_state_delete, so we
+don't call xfrm_state_delete_tunnel for those states, and we end up
+leaking the FB tunnel.
+
+There are several codepaths affected by this: the add/update paths, in
+both net/key and xfrm, and the migrate code (xfrm_migrate,
+xfrm_state_migrate). A "proper" rollback of the init_state work would
+probably be doable in the add/update code, but for migrate it gets
+more complicated as multiple states may be involved.
+
+At some point, the new (not-inserted) state will be destroyed, so call
+xfrm_state_delete_tunnel during xfrm_state_gc_destroy. Most states
+will have their fallback tunnel cleaned up during __xfrm_state_delete,
+which solves the issue that b441cf3f8c4b (and other patches before it)
+aimed at. All states (including FB tunnels) will be removed from the
+lists once xfrm_state_fini has called flush_work(&xfrm_state_gc_work).
+
+Reported-by: syzbot+999eb23467f83f9bf9bf@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=999eb23467f83f9bf9bf
+Fixes: b441cf3f8c4b ("xfrm: delete x->tunnel as we delete x")
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/xfrm/xfrm_state.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index b0e2958342ad2..3d806492e1de9 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -521,6 +521,7 @@ void xfrm_state_free(struct xfrm_state *x)
+ }
+ EXPORT_SYMBOL(xfrm_state_free);
++static void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ static void xfrm_state_gc_destroy(struct xfrm_state *x)
+ {
+       hrtimer_cancel(&x->mtimer);
+@@ -535,6 +536,7 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
+       kfree(x->preplay_esn);
+       if (x->type_offload)
+               xfrm_put_type_offload(x->type_offload);
++      xfrm_state_delete_tunnel(x);
+       if (x->type) {
+               x->type->destructor(x);
+               xfrm_put_type(x->type);
+@@ -731,7 +733,6 @@ void __xfrm_state_destroy(struct xfrm_state *x)
+ }
+ EXPORT_SYMBOL(__xfrm_state_destroy);
+-static void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ int __xfrm_state_delete(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+-- 
+2.51.0
+
diff --git a/queue-6.6/xfrm-delete-x-tunnel-as-we-delete-x.patch b/queue-6.6/xfrm-delete-x-tunnel-as-we-delete-x.patch
new file mode 100644 (file)
index 0000000..26831a7
--- /dev/null
@@ -0,0 +1,196 @@
+From 25795860c394ebbc954e6f1076177ddbc996034e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 29 Jul 2025 17:11:52 -0400
+Subject: xfrm: delete x->tunnel as we delete x
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit b441cf3f8c4b8576639d20c8eb4aa32917602ecd ]
+
+The ipcomp fallback tunnels currently get deleted (from the various
+lists and hashtables) as the last user state that needed that fallback
+is destroyed (not deleted). If a reference to that user state still
+exists, the fallback state will remain on the hashtables/lists,
+triggering the WARN in xfrm_state_fini. Because of those remaining
+references, the fix in commit f75a2804da39 ("xfrm: destroy xfrm_state
+synchronously on net exit path") is not complete.
+
+We recently fixed one such situation in TCP due to defered freeing of
+skbs (commit 9b6412e6979f ("tcp: drop secpath at the same time as we
+currently drop dst")). This can also happen due to IP reassembly: skbs
+with a secpath remain on the reassembly queue until netns
+destruction. If we can't guarantee that the queues are flushed by the
+time xfrm_state_fini runs, there may still be references to a (user)
+xfrm_state, preventing the timely deletion of the corresponding
+fallback state.
+
+Instead of chasing each instance of skbs holding a secpath one by one,
+this patch fixes the issue directly within xfrm, by deleting the
+fallback state as soon as the last user state depending on it has been
+deleted. Destruction will still happen when the final reference is
+dropped.
+
+A separate lockdep class for the fallback state is required since
+we're going to lock x->tunnel while x is locked.
+
+Fixes: 9d4139c76905 ("netns xfrm: per-netns xfrm_state_all list")
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/xfrm.h      |  1 -
+ net/ipv4/ipcomp.c       |  2 ++
+ net/ipv6/ipcomp6.c      |  2 ++
+ net/ipv6/xfrm6_tunnel.c |  2 +-
+ net/xfrm/xfrm_ipcomp.c  |  1 -
+ net/xfrm/xfrm_state.c   | 19 ++++++++-----------
+ 6 files changed, 13 insertions(+), 14 deletions(-)
+
+diff --git a/include/net/xfrm.h b/include/net/xfrm.h
+index 84a1c8c861d29..f7a52b9b56acc 100644
+--- a/include/net/xfrm.h
++++ b/include/net/xfrm.h
+@@ -414,7 +414,6 @@ int xfrm_input_register_afinfo(const struct xfrm_input_afinfo *afinfo);
+ int xfrm_input_unregister_afinfo(const struct xfrm_input_afinfo *afinfo);
+ void xfrm_flush_gc(void);
+-void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ struct xfrm_type {
+       struct module           *owner;
+diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
+index 5a4fb2539b08b..9a45aed508d19 100644
+--- a/net/ipv4/ipcomp.c
++++ b/net/ipv4/ipcomp.c
+@@ -54,6 +54,7 @@ static int ipcomp4_err(struct sk_buff *skb, u32 info)
+ }
+ /* We always hold one tunnel user reference to indicate a tunnel */
++static struct lock_class_key xfrm_state_lock_key;
+ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+@@ -62,6 +63,7 @@ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
+       t = xfrm_state_alloc(net);
+       if (!t)
+               goto out;
++      lockdep_set_class(&t->lock, &xfrm_state_lock_key);
+       t->id.proto = IPPROTO_IPIP;
+       t->id.spi = x->props.saddr.a4;
+diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
+index 72d4858dec18a..8607569de34f3 100644
+--- a/net/ipv6/ipcomp6.c
++++ b/net/ipv6/ipcomp6.c
+@@ -71,6 +71,7 @@ static int ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+       return 0;
+ }
++static struct lock_class_key xfrm_state_lock_key;
+ static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+@@ -79,6 +80,7 @@ static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
+       t = xfrm_state_alloc(net);
+       if (!t)
+               goto out;
++      lockdep_set_class(&t->lock, &xfrm_state_lock_key);
+       t->id.proto = IPPROTO_IPV6;
+       t->id.spi = xfrm6_tunnel_alloc_spi(net, (xfrm_address_t *)&x->props.saddr);
+diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
+index 1323f2f6928e2..1702b4de1c1ef 100644
+--- a/net/ipv6/xfrm6_tunnel.c
++++ b/net/ipv6/xfrm6_tunnel.c
+@@ -334,8 +334,8 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
+       struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
+       unsigned int i;
+-      xfrm_flush_gc();
+       xfrm_state_flush(net, 0, false, true);
++      xfrm_flush_gc();
+       for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
+               WARN_ON_ONCE(!hlist_empty(&xfrm6_tn->spi_byaddr[i]));
+diff --git a/net/xfrm/xfrm_ipcomp.c b/net/xfrm/xfrm_ipcomp.c
+index 9c0fa0e1786a2..f2e70e918f114 100644
+--- a/net/xfrm/xfrm_ipcomp.c
++++ b/net/xfrm/xfrm_ipcomp.c
+@@ -315,7 +315,6 @@ void ipcomp_destroy(struct xfrm_state *x)
+       struct ipcomp_data *ipcd = x->data;
+       if (!ipcd)
+               return;
+-      xfrm_state_delete_tunnel(x);
+       mutex_lock(&ipcomp_resource_mutex);
+       ipcomp_free_data(ipcd);
+       mutex_unlock(&ipcomp_resource_mutex);
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index ded559f557675..7c8aeec4fe274 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -736,6 +736,7 @@ void __xfrm_state_destroy(struct xfrm_state *x, bool sync)
+ }
+ EXPORT_SYMBOL(__xfrm_state_destroy);
++static void xfrm_state_delete_tunnel(struct xfrm_state *x);
+ int __xfrm_state_delete(struct xfrm_state *x)
+ {
+       struct net *net = xs_net(x);
+@@ -756,6 +757,8 @@ int __xfrm_state_delete(struct xfrm_state *x)
+               xfrm_dev_state_delete(x);
++              xfrm_state_delete_tunnel(x);
++
+               /* All xfrm_state objects are created by xfrm_state_alloc.
+                * The xfrm_state_alloc call gives a reference, and that
+                * is what we are dropping here.
+@@ -859,10 +862,7 @@ int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync)
+                               err = xfrm_state_delete(x);
+                               xfrm_audit_state_delete(x, err ? 0 : 1,
+                                                       task_valid);
+-                              if (sync)
+-                                      xfrm_state_put_sync(x);
+-                              else
+-                                      xfrm_state_put(x);
++                              xfrm_state_put(x);
+                               if (!err)
+                                       cnt++;
+@@ -2798,20 +2798,17 @@ void xfrm_flush_gc(void)
+ }
+ EXPORT_SYMBOL(xfrm_flush_gc);
+-/* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */
+-void xfrm_state_delete_tunnel(struct xfrm_state *x)
++static void xfrm_state_delete_tunnel(struct xfrm_state *x)
+ {
+       if (x->tunnel) {
+               struct xfrm_state *t = x->tunnel;
+-              if (atomic_read(&t->tunnel_users) == 2)
++              if (atomic_dec_return(&t->tunnel_users) == 1)
+                       xfrm_state_delete(t);
+-              atomic_dec(&t->tunnel_users);
+-              xfrm_state_put_sync(t);
++              xfrm_state_put(t);
+               x->tunnel = NULL;
+       }
+ }
+-EXPORT_SYMBOL(xfrm_state_delete_tunnel);
+ u32 xfrm_state_mtu(struct xfrm_state *x, int mtu)
+ {
+@@ -2989,8 +2986,8 @@ void xfrm_state_fini(struct net *net)
+       unsigned int sz;
+       flush_work(&net->xfrm.state_hash_work);
+-      flush_work(&xfrm_state_gc_work);
+       xfrm_state_flush(net, 0, false, true);
++      flush_work(&xfrm_state_gc_work);
+       WARN_ON(!list_empty(&net->xfrm.state_all));
+-- 
+2.51.0
+
diff --git a/queue-6.6/xfrm-flush-all-states-in-xfrm_state_fini.patch b/queue-6.6/xfrm-flush-all-states-in-xfrm_state_fini.patch
new file mode 100644 (file)
index 0000000..21aabba
--- /dev/null
@@ -0,0 +1,61 @@
+From 4a06e1b7cbfed4e1a7c9dfd65c5c6a4a630f9109 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 4 Aug 2025 11:05:43 +0200
+Subject: xfrm: flush all states in xfrm_state_fini
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit 42e42562c9cfcdacf000f1b42284a4fad24f8546 ]
+
+While reverting commit f75a2804da39 ("xfrm: destroy xfrm_state
+synchronously on net exit path"), I incorrectly changed
+xfrm_state_flush's "proto" argument back to IPSEC_PROTO_ANY. This
+reverts some of the changes in commit dbb2483b2a46 ("xfrm: clean up
+xfrm protocol checks"), and leads to some states not being removed
+when we exit the netns.
+
+Pass 0 instead of IPSEC_PROTO_ANY from both xfrm_state_fini
+xfrm6_tunnel_net_exit, so that xfrm_state_flush deletes all states.
+
+Fixes: 2a198bbec691 ("Revert "xfrm: destroy xfrm_state synchronously on net exit path"")
+Reported-by: syzbot+6641a61fe0e2e89ae8c5@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=6641a61fe0e2e89ae8c5
+Tested-by: syzbot+6641a61fe0e2e89ae8c5@syzkaller.appspotmail.com
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv6/xfrm6_tunnel.c | 2 +-
+ net/xfrm/xfrm_state.c   | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
+index 3645ab427e128..775ae1171ef89 100644
+--- a/net/ipv6/xfrm6_tunnel.c
++++ b/net/ipv6/xfrm6_tunnel.c
+@@ -334,7 +334,7 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
+       struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
+       unsigned int i;
+-      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
++      xfrm_state_flush(net, 0, false);
+       xfrm_flush_gc();
+       for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index 3d806492e1de9..5396fe6b90c66 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -2982,7 +2982,7 @@ void xfrm_state_fini(struct net *net)
+       unsigned int sz;
+       flush_work(&net->xfrm.state_hash_work);
+-      xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
++      xfrm_state_flush(net, 0, false);
+       flush_work(&xfrm_state_gc_work);
+       WARN_ON(!list_empty(&net->xfrm.state_all));
+-- 
+2.51.0
+