{"id":2222574,"url":"http://patchwork.ozlabs.org/api/1.2/patches/2222574/?format=json","web_url":"http://patchwork.ozlabs.org/project/netfilter-devel/patch/20260412222801.34965-1-marko.jevtic@codereflect.io/","project":{"id":26,"url":"http://patchwork.ozlabs.org/api/1.2/projects/26/?format=json","name":"Netfilter Development","link_name":"netfilter-devel","list_id":"netfilter-devel.vger.kernel.org","list_email":"netfilter-devel@vger.kernel.org","web_url":null,"scm_url":null,"webscm_url":null,"list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<20260412222801.34965-1-marko.jevtic@codereflect.io>","list_archive_url":null,"date":"2026-04-12T22:28:01","name":"[net,v3] netfilter: nft_set_rbtree: fix use count leak on transaction abort","commit_ref":null,"pull_url":null,"state":"not-applicable","archived":false,"hash":"70a04b5b03390e5edef7a45e373c00a7e31b22f5","submitter":{"id":93125,"url":"http://patchwork.ozlabs.org/api/1.2/people/93125/?format=json","name":"Marko Jevtic","email":"marko.jevtic@codereflect.io"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/netfilter-devel/patch/20260412222801.34965-1-marko.jevtic@codereflect.io/mbox/","series":[{"id":499634,"url":"http://patchwork.ozlabs.org/api/1.2/series/499634/?format=json","web_url":"http://patchwork.ozlabs.org/project/netfilter-devel/list/?series=499634","date":"2026-04-12T22:28:01","name":"[net,v3] netfilter: nft_set_rbtree: fix use count leak on transaction abort","version":3,"mbox":"http://patchwork.ozlabs.org/series/499634/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2222574/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2222574/checks/","tags":{},"related":[],"headers":{"Return-Path":"\n <netfilter-devel+bounces-11835-incoming=patchwork.ozlabs.org@vger.kernel.org>","X-Original-To":["incoming@patchwork.ozlabs.org","netfilter-devel@vger.kernel.org"],"Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=codereflect-io.20251104.gappssmtp.com\n header.i=@codereflect-io.20251104.gappssmtp.com header.a=rsa-sha256\n header.s=20251104 header.b=yjgE6hUv;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c15:e001:75::12fc:5321; helo=sin.lore.kernel.org;\n envelope-from=netfilter-devel+bounces-11835-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)","smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=codereflect-io.20251104.gappssmtp.com\n header.i=@codereflect-io.20251104.gappssmtp.com header.b=\"yjgE6hUv\"","smtp.subspace.kernel.org;\n arc=none smtp.client-ip=209.85.128.46","smtp.subspace.kernel.org;\n dmarc=none (p=none dis=none) header.from=codereflect.io","smtp.subspace.kernel.org;\n spf=none smtp.mailfrom=codereflect.io"],"Received":["from sin.lore.kernel.org (sin.lore.kernel.org\n [IPv6:2600:3c15:e001:75::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fv4tm1cMJz1yDF\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 13 Apr 2026 08:28:15 +1000 (AEST)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sin.lore.kernel.org (Postfix) with ESMTP id 3C5F13002928\n\tfor <incoming@patchwork.ozlabs.org>; Sun, 12 Apr 2026 22:28:08 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 0155031B100;\n\tSun, 12 Apr 2026 22:28:07 +0000 (UTC)","from mail-wm1-f46.google.com (mail-wm1-f46.google.com\n [209.85.128.46])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id DE1BD30AD15\n\tfor <netfilter-devel@vger.kernel.org>; Sun, 12 Apr 2026 22:28:04 +0000 (UTC)","by mail-wm1-f46.google.com with SMTP id\n 5b1f17b1804b1-48374014a77so53290505e9.3\n        for <netfilter-devel@vger.kernel.org>;\n Sun, 12 Apr 2026 15:28:04 -0700 (PDT)","from localhost.localdomain (cable-24-135-46-158.dynamic.sbb.rs.\n [24.135.46.158])\n        by smtp.gmail.com with ESMTPSA id\n 5b1f17b1804b1-488d5888a97sm301980325e9.2.2026.04.12.15.28.01\n        (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256);\n        Sun, 12 Apr 2026 15:28:02 -0700 (PDT)"],"ARC-Seal":"i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1776032886; cv=none;\n b=cRPOtmHu8XxgTGdzu79BjBF8TSIexn1CBSbPH1TiSnoaJO2y+Ot2Cu2ycJEFoEZXRgTwrd2BFVD8r+yhMZnlJdlntDZ2W3kcv+G0wM6dw64nqn7ZfARNStAY1YXgWqkZfbTSG9YaDfJPqFxwmAMcxoSOcjZsL6CxefjC4SpGM2U=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1776032886; c=relaxed/simple;\n\tbh=+E7OrLjirfF3etD2wCL8LfLILmHpRwGWXcgXbwUGLjg=;\n\th=From:To:Cc:Subject:Date:Message-ID:MIME-Version;\n b=MyuHyz29G5AvGwjhzX3vZ77zQtpG0PZtxe+ZR9OQrYdcTFYYRZ4DC+oHyRne8q/m7EAO6s0tp9TFJHeKAsdyk1njdejBe8+812wacH9r4igqfSDaYD94WuyKRsvfAiir9+dXwSTUth1+X2ngQMabFFjRiAs4STfgXHQ6g8yBLsg=","ARC-Authentication-Results":"i=1; smtp.subspace.kernel.org;\n dmarc=none (p=none dis=none) header.from=codereflect.io;\n spf=none smtp.mailfrom=codereflect.io;\n dkim=pass (2048-bit key) header.d=codereflect-io.20251104.gappssmtp.com\n header.i=@codereflect-io.20251104.gappssmtp.com header.b=yjgE6hUv;\n arc=none smtp.client-ip=209.85.128.46","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=codereflect-io.20251104.gappssmtp.com; s=20251104; t=1776032883;\n x=1776637683; darn=vger.kernel.org;\n        h=content-transfer-encoding:mime-version:message-id:date:subject:cc\n         :to:from:from:to:cc:subject:date:message-id:reply-to;\n        bh=JymgS9xKzYdXbFenZQ/ZXQb2L7c5DuoUb3it11um76c=;\n        b=yjgE6hUvzGUsMQsQ8IXB7avoWdoTEckRAhHNDHGZaRCv+jrXdPpwjJ81i462jfzTJe\n         CmaASqL00M+LcSZDFoZSeMRSQNEzXIwHpNKHpFejLQDQv2jN25bAeoja6/mjpySjS59P\n         G32Re4syp5J+dK1r788aqAywYSgRdLVSY5Yyms/7QkEF/cbaRLTUbfvni835B/N1xVxo\n         OlHAECF/3golbcgPBt/sDKgXJo8ILXDEAggNJL5AkY3s4hA12A5NbEelxrTLM4xzO5cq\n         2fXA4w1BR41lMET6wf3H4ZAXtI9na9GGbtHR/D0PekY3C5y/A6tx5G6Ljh2m9eSuY7Ih\n         GYvw==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=1e100.net; s=20251104; t=1776032883; x=1776637683;\n        h=content-transfer-encoding:mime-version:message-id:date:subject:cc\n         :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date\n         :message-id:reply-to;\n        bh=JymgS9xKzYdXbFenZQ/ZXQb2L7c5DuoUb3it11um76c=;\n        b=sB+zRkdOEQ2QtKV7fMGWfdZ7ddb5roe/MukCTDTwzf4KfNRizpfgAxoQJUh488biVT\n         tfRYc9TQD8c2hsAqBXVvy3yqnk1kurIiyKGDkBG08fnf/XxDSwZjUJdW2+xXw0RJDoux\n         msjgKwvowaU0VMEZjZzvOr3Qce9WaIpFoysv3r6Vg5LyQs5dYTjpQ5APJxdit+ZH39K4\n         kEGNUpolCLZKwuEWlG4cr4+7jz79yqBFW/eVyPAMYbHrHbe+W6oMNe7L2fabQcl1xLRN\n         Sq2m/qRnvrv4OxPw34qAVp/wsB1sXMnLH0GWsdkJdn/S1dazZXaw2yFE1DRckHaDGSsA\n         AKHQ==","X-Forwarded-Encrypted":"i=1;\n AJvYcCV4Y9lzGrik66JRHHgmpn62h0DPp9rfTRk0XjSFTIcKxzcNEKrUO2FGM59yxSICXawrUYPiQ/edJbmNrcKzzOo=@vger.kernel.org","X-Gm-Message-State":"AOJu0YwDel31nCsxP4kgqbQ6i85IxGalkqa0qgKVpF3Rf2Q+rlKgRZm+\n\tLW9wO2LbDvbXQb+Mt/t77hJK6d+AFbrFdC1aiu9I0mOZe7YlxiMB/wUWjtUM90FKe4M=","X-Gm-Gg":"AeBDiet/+Tekz5le2M1iH1RZSTi6g2oLK3fIc+1QLU955c5BYmI3wZlUQIWC39NCiRD\n\tOhkGCtk6TqW613wl5qcvYgSN7GJJlo/+wRWC9x4Wj6bApnqzsbROCVzMCaM9tFwDDzyCCbtd0+j\n\t9XKA7cyHadazHwiPMyiSVuoYiqSN3vnRfRvp7g/KURyNKjMrWWsIC0EOejj/HXuoc0Qk4D0d1ql\n\t14+YBwXbLiv+Caui4i6E00qCsIgBhq++vI3jTASdeyYsPRWVTw/uinN0IwAPiGdtQNe4GsnVpGe\n\tldoIZ0H0qfyGVcmv4fOU7Fqvg/ghB40FgSJomkIEfvw4MEY/rAkyPlvbcNUm4rMBvP8u+FLk20O\n\t5x6pnlfnLql3ftxhJLjysrlP0Yak23/5SWmGeiB9ZtB/5jfTEh4HYTWHpHdRcoPcd0UZp8cu9vm\n\tpl6wx7YT4vjlpw2DGz11mjZBp484eyVG5HTC/2WZw7C+5XO7riK68/rR6IcOIkemrB9QRJEIrfj\n\t/Qgof/rxChjdrsnX7te31QwDw==","X-Received":"by 2002:a05:600c:a11c:b0:485:303b:c50a with SMTP id\n 5b1f17b1804b1-488d6804721mr111550815e9.13.1776032883203;\n        Sun, 12 Apr 2026 15:28:03 -0700 (PDT)","From":"Marko Jevtic <marko.jevtic@codereflect.io>","To":"pablo@netfilter.org,\n\tfw@strlen.de,\n\tnetfilter-devel@vger.kernel.org","Cc":"phil@nwl.cc,\n\tcoreteam@netfilter.org,\n\tdavem@davemloft.net,\n\tedumazet@google.com,\n\tkuba@kernel.org,\n\tpabeni@redhat.com,\n\thorms@kernel.org,\n\tnetdev@vger.kernel.org,\n\tlinux-kernel@vger.kernel.org","Subject":"[PATCH net v3] netfilter: nft_set_rbtree: fix use count leak on\n transaction abort","Date":"Mon, 13 Apr 2026 00:28:01 +0200","Message-ID":"<20260412222801.34965-1-marko.jevtic@codereflect.io>","X-Mailer":"git-send-email 2.50.1","Precedence":"bulk","X-Mailing-List":"netfilter-devel@vger.kernel.org","List-Id":"<netfilter-devel.vger.kernel.org>","List-Subscribe":"<mailto:netfilter-devel+subscribe@vger.kernel.org>","List-Unsubscribe":"<mailto:netfilter-devel+unsubscribe@vger.kernel.org>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit"},"content":"nft_rbtree_abort() does not handle elements moved to the expired list\nby inline GC during __nft_rbtree_insert(). When inline GC encounters\nexpired elements during overlap detection, it calls\nnft_rbtree_gc_elem_move() which deactivates element data (decrementing\nchain/object use counts), removes the element from the rbtree, and\nqueues it for deferred freeing. On commit, these elements are freed\nvia nft_rbtree_gc_queue(). On abort, however, the expired list is\nignored entirely.\n\nThis leaves use counts permanently decremented after abort.\n\nThis restores transactional semantics by ensuring that inline GC side\neffects are fully rolled back on abort:\n\n- Introduce a separate tx_gc list for elements collected during insert\n  (transaction-scoped), distinct from the existing expired list used\n  by commit-time gc_scan (commit-scoped). This prevents abort from\n  touching committed expired elements left over from a prior gc_queue\n  OOM.\n\n- On commit: splice tx_gc into expired after publishing the new binary\n  search blob, then drain via gc_queue as before.\n\n- On abort: iterate tx_gc, re-activate element data (restoring use\n  counts), and re-insert into the rbtree. Elements remain expired and\n  will be properly collected on the next successful commit.\n\n- Extract nft_rbtree_node_insert() helper from __nft_rbtree_insert()\n  to share the tree insertion logic with the abort restore path.\n\n- Add WARN_ON_ONCE in commit early-return path to catch any violation\n  of the invariant that tx_gc is empty when no tree changes occurred.\n\n- Reset start_rbe_cookie on abort so insertion state from a failed\n  transaction does not persist.\n\nFixes: f6c383b8c31a (\"netfilter: nf_tables: adapt set backend to use GC transaction API\")\nSigned-off-by: Marko Jevtic <marko.jevtic@codereflect.io>\n---\nv3:\n- add Fixes tag\n- narrow the changelog to the abort-side use-count accounting bug\n\nv2:\n- introduce a transaction-scoped tx_gc list for insert-time GC\n- restore tx_gc entries on abort and splice them to expired on commit\n- export nft_setelem_data_activate() and factor out nft_rbtree_node_insert()\n\n include/net/netfilter/nf_tables.h |  3 +\n net/netfilter/nf_tables_api.c     |  4 +-\n net/netfilter/nft_set_rbtree.c    | 96 ++++++++++++++++++++++---------\n 3 files changed, 74 insertions(+), 29 deletions(-)","diff":"diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h\nindex ec8a8ec9c..f8c912332 100644\n--- a/include/net/netfilter/nf_tables.h\n+++ b/include/net/netfilter/nf_tables.h\n@@ -1910,6 +1910,9 @@ struct nft_trans_gc *nft_trans_gc_catchall_async(struct nft_trans_gc *gc,\n \t\t\t\t\t\t unsigned int gc_seq);\n struct nft_trans_gc *nft_trans_gc_catchall_sync(struct nft_trans_gc *gc);\n \n+void nft_setelem_data_activate(const struct net *net,\n+\t\t\t\t const struct nft_set *set,\n+\t\t\t\t struct nft_elem_priv *elem_priv);\n void nft_setelem_data_deactivate(const struct net *net,\n \t\t\t\t const struct nft_set *set,\n \t\t\t\t struct nft_elem_priv *elem_priv);\ndiff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c\nindex 8c42247a1..8e783db3f 100644\n--- a/net/netfilter/nf_tables_api.c\n+++ b/net/netfilter/nf_tables_api.c\n@@ -5837,7 +5837,7 @@ static void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,\n \t}\n }\n \n-static void nft_setelem_data_activate(const struct net *net,\n-\t\t\t\t      const struct nft_set *set,\n-\t\t\t\t      struct nft_elem_priv *elem_priv);\n+void nft_setelem_data_activate(const struct net *net,\n+\t\t\t       const struct nft_set *set,\n+\t\t\t       struct nft_elem_priv *elem_priv);\n \n@@ -7656,7 +7656,7 @@ static int nft_setelem_active_next(const struct net *net,\n \treturn nft_set_elem_active(ext, genmask);\n }\n \n-static void nft_setelem_data_activate(const struct net *net,\n-\t\t\t\t      const struct nft_set *set,\n-\t\t\t\t      struct nft_elem_priv *elem_priv)\n+void nft_setelem_data_activate(const struct net *net,\n+\t\t\t       const struct nft_set *set,\n+\t\t\t       struct nft_elem_priv *elem_priv)\n {\ndiff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c\nindex 737c339de..e1f76d6ef 100644\n--- a/net/netfilter/nft_set_rbtree.c\n+++ b/net/netfilter/nft_set_rbtree.c\n@@ -36,6 +36,7 @@ struct nft_rbtree {\n \tunsigned long\t\tstart_rbe_cookie;\n \tunsigned long\t\tlast_gc;\n \tstruct list_head\texpired;\n+\tstruct list_head\ttx_gc;\n \tu64\t\t\tlast_tstamp;\n };\n \n@@ -194,14 +195,14 @@ nft_rbtree_get(const struct net *net, const struct nft_set *set,\n \n static void nft_rbtree_gc_elem_move(struct net *net, struct nft_set *set,\n \t\t\t\t    struct nft_rbtree *priv,\n-\t\t\t\t    struct nft_rbtree_elem *rbe)\n+\t\t\t\t    struct nft_rbtree_elem *rbe,\n+\t\t\t\t    struct list_head *target_list)\n {\n \tlockdep_assert_held_write(&priv->lock);\n \tnft_setelem_data_deactivate(net, set, &rbe->priv);\n \trb_erase(&rbe->node, &priv->root);\n \n-\t/* collected later on in commit callback */\n-\tlist_add(&rbe->list, &priv->expired);\n+\tlist_add(&rbe->list, target_list);\n }\n \n static const struct nft_rbtree_elem *\n@@ -229,10 +230,10 @@ nft_rbtree_gc_elem(const struct nft_set *__set, struct nft_rbtree *priv,\n \trbe_prev = NULL;\n \tif (prev) {\n \t\trbe_prev = rb_entry(prev, struct nft_rbtree_elem, node);\n-\t\tnft_rbtree_gc_elem_move(net, set, priv, rbe_prev);\n+\t\tnft_rbtree_gc_elem_move(net, set, priv, rbe_prev, &priv->tx_gc);\n \t}\n \n-\tnft_rbtree_gc_elem_move(net, set, priv, rbe);\n+\tnft_rbtree_gc_elem_move(net, set, priv, rbe, &priv->tx_gc);\n \n \treturn rbe_prev;\n }\n@@ -335,6 +336,35 @@ static bool nft_rbtree_insert_same_interval(const struct net *net,\n \treturn false;\n }\n \n+static void nft_rbtree_node_insert(const struct nft_set *set,\n+\t\t\t\t   struct nft_rbtree *priv,\n+\t\t\t\t   struct nft_rbtree_elem *new)\n+{\n+\tstruct nft_rbtree_elem *rbe;\n+\tstruct rb_node *parent, **p;\n+\tint d;\n+\n+\tlockdep_assert_held_write(&priv->lock);\n+\n+\tparent = NULL;\n+\tp = &priv->root.rb_node;\n+\twhile (*p) {\n+\t\tparent = *p;\n+\t\trbe = rb_entry(parent, struct nft_rbtree_elem, node);\n+\t\td = nft_rbtree_cmp(set, rbe, new);\n+\t\tif (d < 0)\n+\t\t\tp = &parent->rb_left;\n+\t\telse if (d > 0)\n+\t\t\tp = &parent->rb_right;\n+\t\telse if (nft_rbtree_interval_end(rbe))\n+\t\t\tp = &parent->rb_left;\n+\t\telse\n+\t\t\tp = &parent->rb_right;\n+\t}\n+\trb_link_node_rcu(&new->node, parent, p);\n+\trb_insert_color(&new->node, &priv->root);\n+}\n+\n static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,\n \t\t\t       struct nft_rbtree_elem *new,\n \t\t\t       struct nft_elem_priv **elem_priv, u64 tstamp)\n@@ -516,25 +546,7 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,\n \t\treturn -ENOTEMPTY;\n \n \t/* Accepted element: pick insertion point depending on key value */\n-\tparent = NULL;\n-\tp = &priv->root.rb_node;\n-\twhile (*p != NULL) {\n-\t\tparent = *p;\n-\t\trbe = rb_entry(parent, struct nft_rbtree_elem, node);\n-\t\td = nft_rbtree_cmp(set, rbe, new);\n-\n-\t\tif (d < 0)\n-\t\t\tp = &parent->rb_left;\n-\t\telse if (d > 0)\n-\t\t\tp = &parent->rb_right;\n-\t\telse if (nft_rbtree_interval_end(rbe))\n-\t\t\tp = &parent->rb_left;\n-\t\telse\n-\t\t\tp = &parent->rb_right;\n-\t}\n-\n-\trb_link_node_rcu(&new->node, parent, p);\n-\trb_insert_color(&new->node, &priv->root);\n+\tnft_rbtree_node_insert(set, priv, new);\n \treturn 0;\n }\n \n@@ -920,11 +932,11 @@ static void nft_rbtree_gc_scan(struct nft_set *set)\n \t\t */\n \t\twrite_lock_bh(&priv->lock);\n \t\tif (rbe_end) {\n-\t\t\tnft_rbtree_gc_elem_move(net, set, priv, rbe_end);\n+\t\t\tnft_rbtree_gc_elem_move(net, set, priv, rbe_end, &priv->expired);\n \t\t\trbe_end = NULL;\n \t\t}\n \n-\t\tnft_rbtree_gc_elem_move(net, set, priv, rbe);\n+\t\tnft_rbtree_gc_elem_move(net, set, priv, rbe, &priv->expired);\n \t\twrite_unlock_bh(&priv->lock);\n \t}\n \n@@ -974,6 +986,7 @@ static int nft_rbtree_init(const struct nft_set *set,\n \trwlock_init(&priv->lock);\n \tpriv->root = RB_ROOT;\n \tINIT_LIST_HEAD(&priv->expired);\n+\tINIT_LIST_HEAD(&priv->tx_gc);\n \n \tpriv->array = NULL;\n \tpriv->array_next = NULL;\n@@ -1000,6 +1013,11 @@ static void nft_rbtree_destroy(const struct nft_ctx *ctx,\n \t\tnf_tables_set_elem_destroy(ctx, set, &rbe->priv);\n \t}\n \n+\tlist_for_each_entry_safe(rbe, next, &priv->tx_gc, list) {\n+\t\tlist_del(&rbe->list);\n+\t\tnf_tables_set_elem_destroy(ctx, set, &rbe->priv);\n+\t}\n+\n \twhile ((node = priv->root.rb_node) != NULL) {\n \t\trb_erase(node, &priv->root);\n \t\trbe = rb_entry(node, struct nft_rbtree_elem, node);\n@@ -1047,8 +1065,10 @@ static void nft_rbtree_commit(struct nft_set *set)\n \tstruct rb_node *node;\n \n \t/* No changes, skip, eg. elements updates only. */\n-\tif (!priv->array_next)\n+\tif (!priv->array_next) {\n+\t\tWARN_ON_ONCE(!list_empty(&priv->tx_gc));\n \t\treturn;\n+\t}\n \n \t/* GC can be performed if the binary search blob is going\n \t * to be rebuilt.  It has to be done in two phases: first\n@@ -1116,13 +1136,35 @@ static void nft_rbtree_commit(struct nft_set *set)\n \t/* New blob is public, queue collected entries for freeing.\n \t * call_rcu ensures elements stay around until readers are done.\n \t */\n+\tlist_splice_tail_init(&priv->tx_gc, &priv->expired);\n \tnft_rbtree_gc_queue(set);\n }\n \n static void nft_rbtree_abort(const struct nft_set *set)\n {\n \tstruct nft_rbtree *priv = nft_set_priv(set);\n+\tstruct nft_rbtree_elem *rbe, *tmp;\n \tstruct nft_array *array_next;\n+\tstruct net *net;\n+\n+\t/* Restore elements that inline GC moved to the tx_gc list during\n+\t * insert: their data was deactivated (use counts decremented) but\n+\t * the transaction was aborted, so re-activate and re-insert to\n+\t * undo GC side effects and restore transactional rollback semantics.\n+\t */\n+\tif (!list_empty(&priv->tx_gc)) {\n+\t\tnet = read_pnet(&set->net);\n+\n+\t\twrite_lock_bh(&priv->lock);\n+\t\tlist_for_each_entry_safe(rbe, tmp, &priv->tx_gc, list) {\n+\t\t\tlist_del_init(&rbe->list);\n+\t\t\tnft_setelem_data_activate(net, set, &rbe->priv);\n+\t\t\tnft_rbtree_node_insert(set, priv, rbe);\n+\t\t}\n+\t\twrite_unlock_bh(&priv->lock);\n+\t}\n+\n+\tpriv->start_rbe_cookie = 0;\n \n \tif (!priv->array_next)\n \t\treturn;\n","prefixes":["net","v3"]}