]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.10-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 23 Mar 2026 13:39:19 +0000 (14:39 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 23 Mar 2026 13:39:19 +0000 (14:39 +0100)
added patches:
netfilter-nft_set_pipapo-split-gc-into-unlink-and-reclaim-phase.patch

queue-5.10/netfilter-nft_set_pipapo-split-gc-into-unlink-and-reclaim-phase.patch [new file with mode: 0644]
queue-5.10/series

diff --git a/queue-5.10/netfilter-nft_set_pipapo-split-gc-into-unlink-and-reclaim-phase.patch b/queue-5.10/netfilter-nft_set_pipapo-split-gc-into-unlink-and-reclaim-phase.patch
new file mode 100644 (file)
index 0000000..883e48d
--- /dev/null
@@ -0,0 +1,196 @@
+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: David Krehwinkel <davidkrehwinkel@gmail.com>
+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    |   43 +++++++++++++++++++++++++++++---------
+ net/netfilter/nft_set_pipapo.h    |    2 +
+ 4 files changed, 40 insertions(+), 15 deletions(-)
+
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -1570,6 +1570,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
+@@ -8334,11 +8334,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
+@@ -1583,11 +1583,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(const struct nft_set *_set, struct nft_pipapo_match *m)
++static void pipapo_gc_scan(const 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);
+@@ -1600,6 +1600,8 @@ static void pipapo_gc(const struct nft_s
+       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];
+               struct nft_pipapo_field *f;
+@@ -1629,9 +1631,13 @@ static void pipapo_gc(const struct nft_s
+               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);
+@@ -1645,9 +1651,21 @@ static void pipapo_gc(const struct nft_s
+               }
+       }
+-      if (gc) {
++      priv->last_gc = jiffies;
++}
++
++/**
++ * pipapo_gc_queue() - Free expired elements after pointer swap
++ * @_set:     nftables API set representation
++ */
++static void pipapo_gc_queue(const struct nft_set *_set)
++{
++      struct nft_pipapo *priv = nft_set_priv(_set);
++      struct nft_trans_gc *gc, *next;
++
++      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;
+       }
+ }
+@@ -1708,14 +1726,14 @@ static void nft_pipapo_commit(const stru
+       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;
++              goto out;
+       new_clone = pipapo_clone(priv->clone);
+       if (IS_ERR(new_clone))
+-              return;
++              goto out;
+       priv->dirty = false;
+@@ -1725,6 +1743,8 @@ static void nft_pipapo_commit(const stru
+               call_rcu(&old->rcu, pipapo_reclaim_match);
+       priv->clone = new_clone;
++out:
++      pipapo_gc_queue(set);
+ }
+ static void nft_pipapo_abort(const struct nft_set *set)
+@@ -2189,6 +2209,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;
+@@ -2241,6 +2262,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 for deferred 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 bf20a5bc34884342030f9358310bac43f37db668..64226a85321f83e2276db4594653ed0eba949f36 100644 (file)
@@ -234,3 +234,4 @@ mtd-rawnand-brcmnand-read-write-oob-during-edu-trans.patch
 mtd-rawnand-brcmnand-move-to-polling-in-pio-mode-on-.patch
 mtd-rawnand-brcmnand-skip-dma-during-panic-write.patch
 tools-bootconfig-fix-fd-leak-in-load_xbc_file-on-fst.patch
+netfilter-nft_set_pipapo-split-gc-into-unlink-and-reclaim-phase.patch