]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
netfilter: nft_set_rbtree: check for partial overlaps in anonymous sets
authorPablo Neira Ayuso <pablo@netfilter.org>
Fri, 6 Feb 2026 12:33:44 +0000 (13:33 +0100)
committerFlorian Westphal <fw@strlen.de>
Fri, 6 Feb 2026 12:36:07 +0000 (13:36 +0100)
Userspace provides an optimized representation in case intervals are
adjacent, where the end element is omitted.

The existing partial overlap detection logic skips anonymous set checks
on start elements for this reason.

However, it is possible to add intervals that overlap to this anonymous
where two start elements with the same, eg. A-B, A-C where C < B.

      start     end
A        B
      start  end
        A     C

Restore the check on overlapping start elements to report an overlap.

Fixes: c9e6978e2725 ("netfilter: nft_set_rbtree: Switch to node list walk for overlap detection")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Florian Westphal <fw@strlen.de>
net/netfilter/nft_set_rbtree.c

index 2c240b0ade870137228b429db11a66ea33bbd80d..14b4256bb00d0e0886c03c696a1f560cc923ca47 100644 (file)
@@ -251,11 +251,23 @@ static bool nft_rbtree_update_first(const struct nft_set *set,
        return false;
 }
 
+/* Only for anonymous sets which do not allow updates, all element are active. */
+static struct nft_rbtree_elem *nft_rbtree_prev_active(struct nft_rbtree_elem *rbe)
+{
+       struct rb_node *node;
+
+       node = rb_prev(&rbe->node);
+       if (!node)
+               return NULL;
+
+       return rb_entry(node, struct nft_rbtree_elem, node);
+}
+
 static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
                               struct nft_rbtree_elem *new,
                               struct nft_elem_priv **elem_priv)
 {
-       struct nft_rbtree_elem *rbe, *rbe_le = NULL, *rbe_ge = NULL;
+       struct nft_rbtree_elem *rbe, *rbe_le = NULL, *rbe_ge = NULL, *rbe_prev;
        struct rb_node *node, *next, *parent, **p, *first = NULL;
        struct nft_rbtree *priv = nft_set_priv(set);
        u8 cur_genmask = nft_genmask_cur(net);
@@ -393,11 +405,19 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
        /* - new start element with existing closest, less or equal key value
         *   being a start element: partial overlap, reported as -ENOTEMPTY.
         *   Anonymous sets allow for two consecutive start element since they
-        *   are constant, skip them to avoid bogus overlap reports.
+        *   are constant, but validate that this new start element does not
+        *   sit in between an existing start and end elements: partial overlap,
+        *   reported as -ENOTEMPTY.
         */
-       if (!nft_set_is_anonymous(set) && rbe_le &&
-           nft_rbtree_interval_start(rbe_le) && nft_rbtree_interval_start(new))
-               return -ENOTEMPTY;
+       if (rbe_le &&
+           nft_rbtree_interval_start(rbe_le) && nft_rbtree_interval_start(new)) {
+               if (!nft_set_is_anonymous(set))
+                       return -ENOTEMPTY;
+
+               rbe_prev = nft_rbtree_prev_active(rbe_le);
+               if (rbe_prev && nft_rbtree_interval_end(rbe_prev))
+                       return -ENOTEMPTY;
+       }
 
        /* - new end element with existing closest, less or equal key value
         *   being a end element: partial overlap, reported as -ENOTEMPTY.