]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.1-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 23 Mar 2026 12:22:46 +0000 (13:22 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 23 Mar 2026 12:22:46 +0000 (13:22 +0100)
added patches:
netfilter-nf_tables-de-constify-set-commit-ops-function-argument.patch
netfilter-nft_set_pipapo-split-gc-into-unlink-and-reclaim-phase.patch

queue-6.1/netfilter-nf_tables-de-constify-set-commit-ops-function-argument.patch [new file with mode: 0644]
queue-6.1/netfilter-nft_set_pipapo-split-gc-into-unlink-and-reclaim-phase.patch [new file with mode: 0644]
queue-6.1/series

diff --git a/queue-6.1/netfilter-nf_tables-de-constify-set-commit-ops-function-argument.patch b/queue-6.1/netfilter-nf_tables-de-constify-set-commit-ops-function-argument.patch
new file mode 100644 (file)
index 0000000..0d2b54e
--- /dev/null
@@ -0,0 +1,56 @@
+From 256001672153af5786c6ca148114693d7d76d836 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 13 Oct 2023 14:18:14 +0200
+Subject: netfilter: nf_tables: de-constify set commit ops function argument
+
+From: Florian Westphal <fw@strlen.de>
+
+commit 256001672153af5786c6ca148114693d7d76d836 upstream.
+
+The set backend using this already has to work around this via ugly
+cast, don't spread this pattern.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/netfilter/nf_tables.h |    2 +-
+ net/netfilter/nft_set_pipapo.c    |    7 +++----
+ 2 files changed, 4 insertions(+), 5 deletions(-)
+
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -470,7 +470,7 @@ struct nft_set_ops {
+                                              const struct nft_set *set,
+                                              const struct nft_set_elem *elem,
+                                              unsigned int flags);
+-      void                            (*commit)(const struct nft_set *set);
++      void                            (*commit)(struct nft_set *set);
+       void                            (*abort)(const struct nft_set *set);
+       u64                             (*privsize)(const struct nlattr * const nla[],
+                                                   const struct nft_set_desc *desc);
+--- a/net/netfilter/nft_set_pipapo.c
++++ b/net/netfilter/nft_set_pipapo.c
+@@ -1587,12 +1587,11 @@ static void nft_pipapo_gc_deactivate(str
+ /**
+  * pipapo_gc() - Drop expired entries from set, destroy start and end elements
+- * @_set:     nftables API set representation
++ * @set:      nftables API set representation
+  * @m:                Matching data
+  */
+-static void pipapo_gc(const struct nft_set *_set, struct nft_pipapo_match *m)
++static void pipapo_gc(struct nft_set *set, struct nft_pipapo_match *m)
+ {
+-      struct nft_set *set = (struct nft_set *) _set;
+       struct nft_pipapo *priv = nft_set_priv(set);
+       struct net *net = read_pnet(&set->net);
+       u64 tstamp = nft_net_tstamp(net);
+@@ -1707,7 +1706,7 @@ static void pipapo_reclaim_match(struct
+  * We also need to create a new working copy for subsequent insertions and
+  * deletions.
+  */
+-static void nft_pipapo_commit(const struct nft_set *set)
++static void nft_pipapo_commit(struct nft_set *set)
+ {
+       struct nft_pipapo *priv = nft_set_priv(set);
+       struct nft_pipapo_match *new_clone, *old;
diff --git a/queue-6.1/netfilter-nft_set_pipapo-split-gc-into-unlink-and-reclaim-phase.patch b/queue-6.1/netfilter-nft_set_pipapo-split-gc-into-unlink-and-reclaim-phase.patch
new file mode 100644 (file)
index 0000000..f487f85
--- /dev/null
@@ -0,0 +1,206 @@
+From 9df95785d3d8302f7c066050117b04cd3c2048c2 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Tue, 3 Mar 2026 16:31:32 +0100
+Subject: netfilter: nft_set_pipapo: split gc into unlink and reclaim phase
+
+From: Florian Westphal <fw@strlen.de>
+
+commit 9df95785d3d8302f7c066050117b04cd3c2048c2 upstream.
+
+Yiming Qian reports Use-after-free in the pipapo set type:
+  Under a large number of expired elements, commit-time GC can run for a very
+  long time in a non-preemptible context, triggering soft lockup warnings and
+  RCU stall reports (local denial of service).
+
+We must split GC in an unlink and a reclaim phase.
+
+We cannot queue elements for freeing until pointers have been swapped.
+Expired elements are still exposed to both the packet path and userspace
+dumpers via the live copy of the data structure.
+
+call_rcu() does not protect us: dump operations or element lookups starting
+after call_rcu has fired can still observe the free'd element, unless the
+commit phase has made enough progress to swap the clone and live pointers
+before any new reader has picked up the old version.
+
+This a similar approach as done recently for the rbtree backend in commit
+35f83a75529a ("netfilter: nft_set_rbtree: don't gc elements on insert").
+
+Fixes: 3c4287f62044 ("nf_tables: Add set type for arbitrary concatenation of ranges")
+Reported-by: Yiming Qian <yimingqian591@gmail.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/netfilter/nf_tables.h |    5 +++
+ net/netfilter/nf_tables_api.c     |    5 ---
+ net/netfilter/nft_set_pipapo.c    |   51 ++++++++++++++++++++++++++++++++------
+ net/netfilter/nft_set_pipapo.h    |    2 +
+ 4 files changed, 50 insertions(+), 13 deletions(-)
+
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -1693,6 +1693,11 @@ struct nft_trans_gc {
+       struct rcu_head         rcu;
+ };
++static inline int nft_trans_gc_space(const struct nft_trans_gc *trans)
++{
++      return NFT_TRANS_GC_BATCHCOUNT - trans->count;
++}
++
+ struct nft_trans_gc *nft_trans_gc_alloc(struct nft_set *set,
+                                       unsigned int gc_seq, gfp_t gfp);
+ void nft_trans_gc_destroy(struct nft_trans_gc *trans);
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -9382,11 +9382,6 @@ static void nft_trans_gc_queue_work(stru
+       schedule_work(&trans_gc_work);
+ }
+-static int nft_trans_gc_space(struct nft_trans_gc *trans)
+-{
+-      return NFT_TRANS_GC_BATCHCOUNT - trans->count;
+-}
+-
+ struct nft_trans_gc *nft_trans_gc_queue_async(struct nft_trans_gc *gc,
+                                             unsigned int gc_seq, gfp_t gfp)
+ {
+--- a/net/netfilter/nft_set_pipapo.c
++++ b/net/netfilter/nft_set_pipapo.c
+@@ -1586,11 +1586,11 @@ static void nft_pipapo_gc_deactivate(str
+ }
+ /**
+- * pipapo_gc() - Drop expired entries from set, destroy start and end elements
++ * pipapo_gc_scan() - Drop expired entries from set and link them to gc list
+  * @set:      nftables API set representation
+  * @m:                Matching data
+  */
+-static void pipapo_gc(struct nft_set *set, struct nft_pipapo_match *m)
++static void pipapo_gc_scan(struct nft_set *set, struct nft_pipapo_match *m)
+ {
+       struct nft_pipapo *priv = nft_set_priv(set);
+       struct net *net = read_pnet(&set->net);
+@@ -1603,6 +1603,8 @@ static void pipapo_gc(struct nft_set *se
+       if (!gc)
+               return;
++      list_add(&gc->list, &priv->gc_head);
++
+       while ((rules_f0 = pipapo_rules_same_key(m->f, first_rule))) {
+               union nft_pipapo_map_bucket rulemap[NFT_PIPAPO_MAX_FIELDS];
+               const struct nft_pipapo_field *f;
+@@ -1632,9 +1634,13 @@ static void pipapo_gc(struct nft_set *se
+               if (__nft_set_elem_expired(&e->ext, tstamp)) {
+                       priv->dirty = true;
+-                      gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC);
+-                      if (!gc)
+-                              return;
++                      if (!nft_trans_gc_space(gc)) {
++                              gc = nft_trans_gc_alloc(set, 0, GFP_KERNEL);
++                              if (!gc)
++                                      return;
++
++                              list_add(&gc->list, &priv->gc_head);
++                      }
+                       nft_pipapo_gc_deactivate(net, set, e);
+                       pipapo_drop(m, rulemap);
+@@ -1648,10 +1654,30 @@ static void pipapo_gc(struct nft_set *se
+               }
+       }
+-      gc = nft_trans_gc_catchall_sync(gc);
++      priv->last_gc = jiffies;
++}
++
++/**
++ * pipapo_gc_queue() - Free expired elements
++ * @set:      nftables API set representation
++ */
++static void pipapo_gc_queue(struct nft_set *set)
++{
++      struct nft_pipapo *priv = nft_set_priv(set);
++      struct nft_trans_gc *gc, *next;
++
++      /* always do a catchall cycle: */
++      gc = nft_trans_gc_alloc(set, 0, GFP_KERNEL);
+       if (gc) {
++              gc = nft_trans_gc_catchall_sync(gc);
++              if (gc)
++                      nft_trans_gc_queue_sync_done(gc);
++      }
++
++      /* always purge queued gc elements. */
++      list_for_each_entry_safe(gc, next, &priv->gc_head, list) {
++              list_del(&gc->list);
+               nft_trans_gc_queue_sync_done(gc);
+-              priv->last_gc = jiffies;
+       }
+ }
+@@ -1705,6 +1731,10 @@ static void pipapo_reclaim_match(struct
+  *
+  * We also need to create a new working copy for subsequent insertions and
+  * deletions.
++ *
++ * After the live copy has been replaced by the clone, we can safely queue
++ * expired elements that have been collected by pipapo_gc_scan() for
++ * memory reclaim.
+  */
+ static void nft_pipapo_commit(struct nft_set *set)
+ {
+@@ -1712,7 +1742,7 @@ static void nft_pipapo_commit(struct nft
+       struct nft_pipapo_match *new_clone, *old;
+       if (time_after_eq(jiffies, priv->last_gc + nft_set_gc_interval(set)))
+-              pipapo_gc(set, priv->clone);
++              pipapo_gc_scan(set, priv->clone);
+       if (!priv->dirty)
+               return;
+@@ -1729,6 +1759,8 @@ static void nft_pipapo_commit(struct nft
+               call_rcu(&old->rcu, pipapo_reclaim_match);
+       priv->clone = new_clone;
++
++      pipapo_gc_queue(set);
+ }
+ static bool nft_pipapo_transaction_mutex_held(const struct nft_set *set)
+@@ -2204,6 +2236,7 @@ static int nft_pipapo_init(const struct
+       priv->dirty = false;
++      INIT_LIST_HEAD(&priv->gc_head);
+       rcu_assign_pointer(priv->match, m);
+       return 0;
+@@ -2256,6 +2289,8 @@ static void nft_pipapo_destroy(const str
+       struct nft_pipapo_match *m;
+       int cpu;
++      WARN_ON_ONCE(!list_empty(&priv->gc_head));
++
+       m = rcu_dereference_protected(priv->match, true);
+       if (m) {
+               rcu_barrier();
+--- a/net/netfilter/nft_set_pipapo.h
++++ b/net/netfilter/nft_set_pipapo.h
+@@ -165,6 +165,7 @@ struct nft_pipapo_match {
+  * @width:    Total bytes to be matched for one packet, including padding
+  * @dirty:    Working copy has pending insertions or deletions
+  * @last_gc:  Timestamp of last garbage collection run, jiffies
++ * @gc_head:  list of nft_trans_gc to queue up for mem reclaim
+  */
+ struct nft_pipapo {
+       struct nft_pipapo_match __rcu *match;
+@@ -172,6 +173,7 @@ struct nft_pipapo {
+       int width;
+       bool dirty;
+       unsigned long last_gc;
++      struct list_head gc_head;
+ };
+ struct nft_pipapo_elem;
index e8d5bce9a60cb6300eb1ac3e696ef256304f879b..912c5109a709deeb449c4f25e23b628ce95b776c 100644 (file)
@@ -451,3 +451,5 @@ ksmbd-fix-use-after-free-of-share_conf-in-compound-r.patch
 drm-i915-gt-check-set_default_submission-before-defe.patch
 lib-bootconfig-check-xbc_init_node-return-in-overrid.patch
 tools-bootconfig-fix-fd-leak-in-load_xbc_file-on-fst.patch
+netfilter-nf_tables-de-constify-set-commit-ops-function-argument.patch
+netfilter-nft_set_pipapo-split-gc-into-unlink-and-reclaim-phase.patch