{"id":2221762,"url":"http://patchwork.ozlabs.org/api/1.2/patches/2221762/?format=json","web_url":"http://patchwork.ozlabs.org/project/netfilter-devel/patch/20260410101321.915190-2-bestswngs@gmail.com/","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":"<20260410101321.915190-2-bestswngs@gmail.com>","list_archive_url":null,"date":"2026-04-10T10:13:22","name":"[nf] netfilter: nf_tables: use RCU-safe list primitives for basechain hook list","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"69471aff5f8e0abc4a80bd87e339abb3ebf99ee3","submitter":{"id":92941,"url":"http://patchwork.ozlabs.org/api/1.2/people/92941/?format=json","name":"Weiming Shi","email":"bestswngs@gmail.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/netfilter-devel/patch/20260410101321.915190-2-bestswngs@gmail.com/mbox/","series":[{"id":499436,"url":"http://patchwork.ozlabs.org/api/1.2/series/499436/?format=json","web_url":"http://patchwork.ozlabs.org/project/netfilter-devel/list/?series=499436","date":"2026-04-10T10:13:22","name":"[nf] netfilter: nf_tables: use RCU-safe list primitives for basechain hook list","version":1,"mbox":"http://patchwork.ozlabs.org/series/499436/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2221762/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2221762/checks/","tags":{},"related":[],"headers":{"Return-Path":"\n <netfilter-devel+bounces-11791-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=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=ILEd90kr;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c09:e001:a7::12fc:5321; helo=sto.lore.kernel.org;\n envelope-from=netfilter-devel+bounces-11791-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)","smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com\n header.b=\"ILEd90kr\"","smtp.subspace.kernel.org;\n arc=none smtp.client-ip=209.85.210.175","smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com","smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=gmail.com"],"Received":["from sto.lore.kernel.org (sto.lore.kernel.org\n [IPv6:2600:3c09:e001:a7::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 4fsXhg4Mccz1y2d\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 10 Apr 2026 20:14:11 +1000 (AEST)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sto.lore.kernel.org (Postfix) with ESMTP id 9BD84300B444\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 10 Apr 2026 10:14:07 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id C8E273ACA75;\n\tFri, 10 Apr 2026 10:14:03 +0000 (UTC)","from mail-pf1-f175.google.com (mail-pf1-f175.google.com\n [209.85.210.175])\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 0BD4F332608\n\tfor <netfilter-devel@vger.kernel.org>; Fri, 10 Apr 2026 10:14:02 +0000 (UTC)","by mail-pf1-f175.google.com with SMTP id\n d2e1a72fcca58-82d561b3689so858917b3a.0\n        for <netfilter-devel@vger.kernel.org>;\n Fri, 10 Apr 2026 03:14:01 -0700 (PDT)","from SLSGDTSWING002.tail0ac356.ts.net ([129.126.109.177])\n        by smtp.gmail.com with ESMTPSA id\n d2e1a72fcca58-82f0c4d5413sm2206692b3a.40.2026.04.10.03.13.57\n        (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n        Fri, 10 Apr 2026 03:14:00 -0700 (PDT)"],"ARC-Seal":"i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1775816043; cv=none;\n b=FVBflVkLJZbYTgH3dMn0AP1sewDGABvnlMZEtUlFFLx4+41rqhbV4EGLW2XaZFzWbtAAyu2SQDTMshUvZ5XAFVuL3r8WbwZs3XoVhQpXqv7Bw9aoefJ18AkM+doIFnMPz78lurVwvLi5i90hYG6akuNz0+VHnhpViuExwBHwPts=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1775816043; c=relaxed/simple;\n\tbh=+5JZ9xXoRUN85ru0donIjNuo+Waq/MzwyOIjJguYJz0=;\n\th=From:To:Cc:Subject:Date:Message-ID:MIME-Version;\n b=emMDnC1IOivRPxf6TAy+NwmQKXX3zHM/jWYVnOUMaG3eMmA+wzMXIeNimC3SHqhSTXZqPEhvG6SSvYsBei+XiQtY8tczhtwcg25H/zFBaT/sYTVFWQW5Wte5500THEiwCVzNS97gWSDblAOAMHej/M6oJedpMId1+LMqO2rW1yQ=","ARC-Authentication-Results":"i=1; smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com;\n spf=pass smtp.mailfrom=gmail.com;\n dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com\n header.b=ILEd90kr; arc=none smtp.client-ip=209.85.210.175","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=gmail.com; s=20251104; t=1775816041; x=1776420841;\n 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=5P6xJCvneuwhXql1CKN1XhB39jxxN+O7rcFH8Ar99kQ=;\n        b=ILEd90krBc8fLzNvMpYMGxRSWS4Ejfm8Tafav2A49vFnVQWe+DjtRS6Ak8z6+BXt9x\n         5FkXaLqttn4Rd0+WLdUlELZfqUTIQgEaO4Dv9yg6navjKQuQly5a292kLEma184Ke/TD\n         JRzNUsXa8ENOHP77iSWpgHo6+/4pLXIgSzoyxouyzKVUtsa/p+Z6RU/L5sRcbZibB5D5\n         XyTt8Vxo/u3lIfzDvHxna2DWV4JCO0ioQFGe2T6NzejI0wJHA1X1YYH4IIdvGf8fMtK0\n         X9NQMnEUUa+9majKJ3RqCst1IBeKnXUHhBZwL/myhi8NcbqP4O/ZXp8gei5uNU74VRdU\n         lOcw==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=1e100.net; s=20251104; t=1775816041; x=1776420841;\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=5P6xJCvneuwhXql1CKN1XhB39jxxN+O7rcFH8Ar99kQ=;\n        b=ijVMvcl2482oaCRos35/q8MZ7APdcdubXjF5ZSps3YjkuNNDmhfQ1lpPA4wuPPv8WJ\n         oimLaT0g9XINb/DjcMoC2U+9xKm3lf3b0d3/c23YH2QaD9Mw+KU58SfWsW16dTtqQzBU\n         +e2Onk4rnu+BuPWJ8cIAdzVV7jd5FJZD7e+JSCh6yu+V5yUQ+AkZ8Pp6jOLNfte1iTuc\n         0f9sYt2WxkhVjWqCwUsJQhuEzwtLowWMc9XSMmrj0b0ljvK+d1cSJEB/ce6GeisQAN8T\n         5ieEG3XSCcMt1B/tAtEHjROGlGaVyQMfiOce391RsppVkDs7DbvKu+oyT5DgTAcppqd8\n         mWsQ==","X-Forwarded-Encrypted":"i=1;\n AJvYcCWLigwEQx0kCqi3r4haLMVOjELokHtTxYENL4qXXOCw8DEZqBplqg5GWLm7MI+NfvTVgoFB3D6aydEbtG9uoYQ=@vger.kernel.org","X-Gm-Message-State":"AOJu0YzgSspvyLw2oPz93b59mrXIRyajlsDD8itWf59xVWBVzhh3KILz\n\t/ujlEQTXPazVTI+fwIY3flFzkkq6yOvPcqZvBKUPaLk2C9BaQvWyUkHa","X-Gm-Gg":"AeBDievLKWVyQlwXLVmfHHQ7fNo5ghV7zB/9dkeWOp3E7VJPT2lyc3UGUnLm0uZRdqe\n\tiJugz6C0Z+6aMufzV9TYC6pZKB3tXO9Sj2M+Rtu/5++KoulSYlktnWykg4+JBNDxfLoIM33NI/Y\n\tcSKaxIZ7e7bvbzLaFNaSO85QOozx8pHEYf0aHC7CnXtJsf+WD7T+826DOlmxHE5zTBaN7CMNz3t\n\tpmkRPZRhCdJvieL9NEvHCA8mWeuvaxVTxPLP6ymzKfxXmEOfseSs+yFGC5fw6I0qfb4jf/4+q8P\n\t3jE+HFfXDRh10U0KxwlKlv+BKTvlNX0aFu6VTyr1zLNV/W5NYCxGPW1Exk7ydtL1uSN/m6wCted\n\tvCB50czURKOyLIC2myZv08ghQ39pTKQd2HUZh9MmAJQ8RUjA/CzBF1T5mujtt4s0sKeAiTuH9T8\n\tBu1cAP59rlgy20IyYu+VY5ozm7xsozSR4IHGRDvJqkiQ2lG7d8z9jrALfkoggsqKJpb0AxWjdNX\n\tFJLuZBVrrMq","X-Received":"by 2002:a05:6a00:12ca:b0:82f:5a4:aa46 with SMTP id\n d2e1a72fcca58-82f0c2b510cmr2621656b3a.44.1775816041376;\n        Fri, 10 Apr 2026 03:14:01 -0700 (PDT)","From":"Weiming Shi <bestswngs@gmail.com>","To":"Pablo Neira Ayuso <pablo@netfilter.org>,\n\tFlorian Westphal <fw@strlen.de>,\n\t\"David S . Miller\" <davem@davemloft.net>,\n\tEric Dumazet <edumazet@google.com>,\n\tJakub Kicinski <kuba@kernel.org>,\n\tPaolo Abeni <pabeni@redhat.com>","Cc":"Phil Sutter <phil@nwl.cc>,\n\tSimon Horman <horms@kernel.org>,\n\tnetfilter-devel@vger.kernel.org,\n\tcoreteam@netfilter.org,\n\tnetdev@vger.kernel.org,\n\tlinux-kernel@vger.kernel.org,\n\tXiang Mei <xmei5@asu.edu>,\n\tWeiming Shi <bestswngs@gmail.com>","Subject":"[PATCH nf] netfilter: nf_tables: use RCU-safe list primitives for\n basechain hook list","Date":"Fri, 10 Apr 2026 18:13:22 +0800","Message-ID":"<20260410101321.915190-2-bestswngs@gmail.com>","X-Mailer":"git-send-email 2.43.0","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_MSG_GETCHAIN runs as an NFNL_CB_RCU callback, so chain dumps\ntraverse basechain->hook_list under rcu_read_lock() without holding\ncommit_mutex. Meanwhile, nft_delchain_hook() mutates that same live\nhook_list with plain list_move() and list_splice(), and the commit/abort\npaths splice hooks back with plain list_splice(). None of these are\nRCU-safe list operations.\n\nA concurrent GETCHAIN dump can observe partially updated list pointers,\nfollow them into stack-local or transaction-private list heads, and\ncrash when container_of() produces a bogus struct nft_hook pointer.\n\nThe PoC triggers this by racing GETCHAIN dumps against aborting DELCHAIN\nhook updates, reachable from an unprivileged user namespace since all\ncapability checks use ns_capable() with CONFIG_NF_TABLES=y (default):\n\n Oops: general protection fault, probably for non-canonical address 0xdffffc0000000006: 0000 [#1] SMP KASAN NOPTI\n KASAN: null-ptr-deref in range [0x0000000000000030-0x0000000000000037]\n RIP: 0010:strlen (lib/string.c:420 (discriminator 1))\n Call Trace:\n  <TASK>\n  nf_tables_fill_chain_info (net/netfilter/nf_tables_api.c:1987 (discriminator 1) net/netfilter/nf_tables_api.c:1992 (discriminator 1) net/netfilter/nf_tables_api.c:2028 (discriminator 1) net/netfilter/nf_tables_api.c:2077 (discriminator 1))\n  nf_tables_dump_chains (net/netfilter/nf_tables_api.c:2173 (discriminator 1))\n  netlink_dump (net/netlink/af_netlink.c:2325 (discriminator 1))\n  __netlink_dump_start (net/netlink/af_netlink.c:2442)\n  nf_tables_getchain (net/netfilter/nf_tables_api.c:1314 net/netfilter/nf_tables_api.c:2212)\n  nfnetlink_rcv_msg (net/netfilter/nfnetlink.c:290)\n  netlink_rcv_skb (net/netlink/af_netlink.c:2550)\n  nfnetlink_rcv (net/netfilter/nfnetlink.c:653)\n  netlink_unicast (net/netlink/af_netlink.c:1319 net/netlink/af_netlink.c:1344)\n  netlink_sendmsg (net/netlink/af_netlink.c:1894)\n  __sys_sendto (net/socket.c:727 net/socket.c:742 net/socket.c:2206)\n  __x64_sys_sendto (net/socket.c:2209)\n  </TASK>\n\nReplace list_move() in nft_delchain_hook() with list_del_rcu() plus an\nintermediate pointer array, followed by synchronize_rcu() before the\ndeleted hooks' list pointers are reused to link them into the\ntransaction's private list. In the error paths, put hooks back with\nlist_add_tail_rcu() which is safe for concurrent RCU readers (they\neither continue to the original successor or see the list head and\nterminate the walk).\n\nAdd nft_hook_list_splice_rcu() helper that splices entries from a\nprivate list into a live RCU-protected list using individual\nlist_add_tail_rcu() calls instead of plain list_splice(). Use it in\nthe commit and abort paths for NEWCHAIN updates and DELCHAIN rollback.\n\nFixes: 7d937b107108 (\"netfilter: nf_tables: support for deleting devices in an existing netdev chain\")\nReported-by: Xiang Mei <xmei5@asu.edu>\nSigned-off-by: Weiming Shi <bestswngs@gmail.com>\n---\n net/netfilter/nf_tables_api.c | 64 ++++++++++++++++++++++++++++++-----\n 1 file changed, 56 insertions(+), 8 deletions(-)","diff":"diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c\nindex 8c42247a176c7..62fcfefba7b0f 100644\n--- a/net/netfilter/nf_tables_api.c\n+++ b/net/netfilter/nf_tables_api.c\n@@ -391,6 +391,22 @@ static void nft_netdev_unregister_hooks(struct net *net,\n \t}\n }\n \n+/* Splice hooks from a private list into a live (RCU-protected) hook list.\n+ * Each entry is published individually via list_add_tail_rcu() so that\n+ * concurrent RCU readers walking the destination list never observe torn\n+ * list pointers.\n+ */\n+static void nft_hook_list_splice_rcu(struct list_head *from,\n+\t\t\t\t     struct list_head *to)\n+{\n+\tstruct nft_hook *hook, *next;\n+\n+\tlist_for_each_entry_safe(hook, next, from, list) {\n+\t\tlist_del(&hook->list);\n+\t\tlist_add_tail_rcu(&hook->list, to);\n+\t}\n+}\n+\n static int nf_tables_register_hook(struct net *net,\n \t\t\t\t   const struct nft_table *table,\n \t\t\t\t   struct nft_chain *chain)\n@@ -3162,9 +3178,11 @@ static int nft_delchain_hook(struct nft_ctx *ctx,\n \tconst struct nlattr * const *nla = ctx->nla;\n \tstruct nft_chain_hook chain_hook = {};\n \tstruct nft_hook *this, *hook;\n+\tstruct nft_hook **del_hooks;\n \tLIST_HEAD(chain_del_list);\n \tstruct nft_trans *trans;\n-\tint err;\n+\tint err, n = 0, i;\n+\tint max_hooks = 0;\n \n \tif (ctx->table->flags & __NFT_TABLE_F_UPDATE)\n \t\treturn -EOPNOTSUPP;\n@@ -3174,19 +3192,38 @@ static int nft_delchain_hook(struct nft_ctx *ctx,\n \tif (err < 0)\n \t\treturn err;\n \n+\tlist_for_each_entry(this, &chain_hook.list, list)\n+\t\tmax_hooks++;\n+\n+\tdel_hooks = kcalloc(max_hooks, sizeof(*del_hooks), GFP_KERNEL);\n+\tif (!del_hooks) {\n+\t\tnft_chain_release_hook(&chain_hook);\n+\t\treturn -ENOMEM;\n+\t}\n+\n \tlist_for_each_entry(this, &chain_hook.list, list) {\n \t\thook = nft_hook_list_find(&basechain->hook_list, this);\n \t\tif (!hook) {\n \t\t\terr = -ENOENT;\n \t\t\tgoto err_chain_del_hook;\n \t\t}\n-\t\tlist_move(&hook->list, &chain_del_list);\n+\t\tlist_del_rcu(&hook->list);\n+\t\tdel_hooks[n++] = hook;\n \t}\n \n+\t/* Wait for any concurrent RCU readers (e.g. GETCHAIN dumps walking\n+\t * basechain->hook_list) to finish before modifying the removed hooks'\n+\t * list pointers to link them into the transaction's private list.\n+\t */\n+\tsynchronize_rcu();\n+\n+\tfor (i = 0; i < n; i++)\n+\t\tlist_add_tail(&del_hooks[i]->list, &chain_del_list);\n+\n \ttrans = nft_trans_alloc_chain(ctx, NFT_MSG_DELCHAIN);\n \tif (!trans) {\n \t\terr = -ENOMEM;\n-\t\tgoto err_chain_del_hook;\n+\t\tgoto err_chain_add_back;\n \t}\n \n \tnft_trans_basechain(trans) = basechain;\n@@ -3194,13 +3231,24 @@ static int nft_delchain_hook(struct nft_ctx *ctx,\n \tINIT_LIST_HEAD(&nft_trans_chain_hooks(trans));\n \tlist_splice(&chain_del_list, &nft_trans_chain_hooks(trans));\n \tnft_chain_release_hook(&chain_hook);\n+\tkfree(del_hooks);\n \n \tnft_trans_commit_list_add_tail(ctx->net, trans);\n \n \treturn 0;\n \n+err_chain_add_back:\n+\tfor (i = 0; i < n; i++)\n+\t\tlist_add_tail_rcu(&del_hooks[i]->list, &basechain->hook_list);\n+\tkfree(del_hooks);\n+\tnft_chain_release_hook(&chain_hook);\n+\n+\treturn err;\n+\n err_chain_del_hook:\n-\tlist_splice(&chain_del_list, &basechain->hook_list);\n+\tfor (i = 0; i < n; i++)\n+\t\tlist_add_tail_rcu(&del_hooks[i]->list, &basechain->hook_list);\n+\tkfree(del_hooks);\n \tnft_chain_release_hook(&chain_hook);\n \n \treturn err;\n@@ -10912,8 +10960,8 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)\n \t\t\t\tnft_chain_commit_update(nft_trans_container_chain(trans));\n \t\t\t\tnf_tables_chain_notify(&ctx, NFT_MSG_NEWCHAIN,\n \t\t\t\t\t\t       &nft_trans_chain_hooks(trans));\n-\t\t\t\tlist_splice(&nft_trans_chain_hooks(trans),\n-\t\t\t\t\t    &nft_trans_basechain(trans)->hook_list);\n+\t\t\t\tnft_hook_list_splice_rcu(&nft_trans_chain_hooks(trans),\n+\t\t\t\t\t\t\t&nft_trans_basechain(trans)->hook_list);\n \t\t\t\t/* trans destroyed after rcu grace period */\n \t\t\t} else {\n \t\t\t\tnft_chain_commit_drop_policy(nft_trans_container_chain(trans));\n@@ -11231,8 +11279,8 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)\n \t\tcase NFT_MSG_DELCHAIN:\n \t\tcase NFT_MSG_DESTROYCHAIN:\n \t\t\tif (nft_trans_chain_update(trans)) {\n-\t\t\t\tlist_splice(&nft_trans_chain_hooks(trans),\n-\t\t\t\t\t    &nft_trans_basechain(trans)->hook_list);\n+\t\t\t\tnft_hook_list_splice_rcu(&nft_trans_chain_hooks(trans),\n+\t\t\t\t\t\t\t&nft_trans_basechain(trans)->hook_list);\n \t\t\t} else {\n \t\t\t\tnft_use_inc_restore(&table->use);\n \t\t\t\tnft_clear(trans->net, nft_trans_chain(trans));\n","prefixes":["nf"]}