From: Vastargazing <vebohr@gmail.com>
To: pablo@netfilter.org, fw@strlen.de
Cc: netfilter-devel@vger.kernel.org, linux-kselftest@vger.kernel.org,
shuah@kernel.org, linux-kernel@vger.kernel.org,
Vastargazing <vebohr@gmail.com>
Subject: [PATCH 1/1] selftests: netfilter: add nft_ct timeout destroy race test
Date: Wed, 22 Apr 2026 16:18:18 +0300 [thread overview]
Message-ID: <20260422131818.106417-2-vebohr@gmail.com> (raw)
In-Reply-To: <20260422131818.106417-1-vebohr@gmail.com>
Add a netfilter kselftest for the nft_ct timeout object destroy race
fixed by commit f8dca15a1b19 ("netfilter: nft_ct: fix use-after-free in
timeout object destroy").
Keep creating new TCP connections from one namespace while repeatedly
flushing and recreating the table that owns a ct timeout object. This
exercises concurrent packet processing against the timeout object
teardown path without requiring external traffic tools beyond bash,
nft and ip.
On a KASAN kernel, a regression in the RCU lifetime handling should
show up as a slab-use-after-free report in nf_conntrack_tcp_packet().
Assisted-by: GitHub Copilot:claude-sonnet-4-6
Signed-off-by: Vastargazing <vebohr@gmail.com>
---
.../testing/selftests/net/netfilter/Makefile | 1 +
.../netfilter/nft_ct_timeout_concurrency.sh | 116 ++++++++++++++++++
2 files changed, 117 insertions(+)
create mode 100644 tools/testing/selftests/net/netfilter/nft_ct_timeout_concurrency.sh
diff --git a/tools/testing/selftests/net/netfilter/Makefile b/tools/testing/selftests/net/netfilter/Makefile
index ee2d1a5254f8..bcf53a1ef7ec 100644
--- a/tools/testing/selftests/net/netfilter/Makefile
+++ b/tools/testing/selftests/net/netfilter/Makefile
@@ -25,6 +25,7 @@ TEST_PROGS := \
nft_audit.sh \
nft_concat_range.sh \
nft_conntrack_helper.sh \
+ nft_ct_timeout_concurrency.sh \
nft_fib.sh \
nft_flowtable.sh \
nft_interface_stress.sh \
diff --git a/tools/testing/selftests/net/netfilter/nft_ct_timeout_concurrency.sh b/tools/testing/selftests/net/netfilter/nft_ct_timeout_concurrency.sh
new file mode 100644
index 000000000000..79876cdfb2df
--- /dev/null
+++ b/tools/testing/selftests/net/netfilter/nft_ct_timeout_concurrency.sh
@@ -0,0 +1,116 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Stress nftables ct timeout object destruction while new TCP flows keep
+# attaching the object.
+
+net_netfilter_dir=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")
+source "$net_netfilter_dir/lib.sh"
+
+checktool "nft --version" "run test without nft tool"
+
+read kernel_tainted < /proc/sys/kernel/tainted
+
+# Default to 80% of the global timeout but keep this stress test short.
+TEST_RUNTIME=$((${kselftest_timeout:-30} * 8 / 10))
+[[ $TEST_RUNTIME -gt 20 ]] && TEST_RUNTIME=20
+
+PORT=12345
+
+cleanup()
+{
+ cleanup_all_ns
+}
+
+load_ruleset()
+{
+ ip netns exec "$ns1" nft -f - <<EOF
+table ip ct_test {
+ ct timeout tcptime {
+ protocol tcp
+ policy = { established: 5s }
+ }
+
+ chain output {
+ type filter hook output priority filter; policy accept;
+ ct state new ip daddr 10.0.1.2 tcp dport $PORT counter ct timeout set "tcptime"
+ }
+}
+EOF
+}
+
+flush_table()
+{
+ ip netns exec "$ns1" nft flush table ip ct_test 2>/dev/null || true
+ ip netns exec "$ns1" nft delete table ip ct_test 2>/dev/null || true
+}
+
+rule_packets()
+{
+ local packets
+
+ packets=$(ip netns exec "$ns1" nft list chain ip ct_test output 2>/dev/null |
+ sed -n 's/.*counter packets \([0-9][0-9]*\) bytes.*/\1/p' |
+ head -n1)
+
+ if [ -n "$packets" ]; then
+ echo "$packets"
+ else
+ echo 0
+ fi
+}
+
+trap cleanup EXIT
+
+setup_ns ns1 ns2 || exit $ksft_skip
+
+if ! ip link add veth0 netns "$ns1" type veth peer name veth0 netns "$ns2" > /dev/null 2>&1; then
+ echo "SKIP: No virtual ethernet pair device support in kernel"
+ exit $ksft_skip
+fi
+
+ip -net "$ns1" link set veth0 up
+ip -net "$ns2" link set veth0 up
+
+ip -net "$ns1" addr add 10.0.1.1/24 dev veth0
+ip -net "$ns2" addr add 10.0.1.2/24 dev veth0
+
+if ! load_ruleset; then
+ echo "SKIP: Could not load ct timeout ruleset"
+ exit $ksft_skip
+fi
+
+ip netns exec "$ns1" bash -c '
+ while :; do
+ exec 3<>/dev/tcp/10.0.1.2/'"$PORT"' 2>/dev/null || true
+ exec 3<&- 3>&-
+ done
+' > /dev/null 2>&1 &
+traffic_pid=$!
+
+if ! busywait_for_counter "$BUSYWAIT_TIMEOUT" 1 rule_packets > /dev/null; then
+ echo "FAIL: Did not observe TCP traffic hitting ct timeout rule"
+ exit $ksft_fail
+fi
+
+end_time=$((SECONDS + TEST_RUNTIME))
+while [ "$SECONDS" -lt "$end_time" ]; do
+ flush_table
+
+ if ! load_ruleset; then
+ echo "FAIL: Could not recreate ct timeout ruleset"
+ exit $ksft_fail
+ fi
+done
+
+flush_table
+
+kill "$traffic_pid" 2>/dev/null
+wait "$traffic_pid" 2>/dev/null
+
+if [[ $kernel_tainted -eq 0 && $(</proc/sys/kernel/tainted) -ne 0 ]]; then
+ echo "FAIL: Kernel is tainted"
+ exit $ksft_fail
+fi
+
+exit $ksft_pass
--
2.51.0
next prev parent reply other threads:[~2026-04-22 13:18 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-22 13:18 [PATCH 0/1] selftests: netfilter: add regression test for nft_ct timeout UAF Vastargazing
2026-04-22 13:18 ` Vastargazing [this message]
2026-04-22 13:51 ` [PATCH 1/1] selftests: netfilter: add nft_ct timeout destroy race test Florian Westphal
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260422131818.106417-2-vebohr@gmail.com \
--to=vebohr@gmail.com \
--cc=fw@strlen.de \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=netfilter-devel@vger.kernel.org \
--cc=pablo@netfilter.org \
--cc=shuah@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox