Skip to content

Commit 173c10a

Browse files
Vastargazingintel-lab-lkp
authored andcommitted
selftests: mptcp: add test for IPv6 subflow SLAB placement
Add mptcp_v6_initcall.sh to verify that MPTCP IPv6 subflow child sockets are allocated from the TCPv6 SLAB cache, not the kmalloc-4k fallback. tcpv6_prot_override must copy tcpv6_prot after proto_register(&tcpv6_prot) populates tcpv6_prot.slab. If the copy runs too early, override.slab stays NULL (frozen by __ro_after_init) and subflow children fall back to kmalloc-4k. This lacks SLAB_TYPESAFE_BY_RCU, allowing lockless ehash lookups in __inet_lookup_established to read freed memory. The test exercises the IPv6 accept path via MPTCP connections between two network namespaces, then checks that the TCPv6 slab active object count grew. On a fixed kernel, the delta is ~2 * NR_CONNS (one subflow per side per connection); on a broken kernel, it stays near zero because children land in kmalloc-4k instead. Topology: two netns connected via veth pair with /64 ULA addresses; NR_CONNS parallel short-lived MPTCP connections are established and held open long enough to sample /proc/slabinfo. The test skips if CONFIG_MPTCP_IPV6 is absent (checked via kallsyms) or /proc/slabinfo is unreadable. Verified on Ubuntu 6.17 kernel predating the fix: TAP "not ok 1 ... TCPv6 slab gains MPTCPv6 subflow children" with delta=0. On kernels with the fix, delta is well above the threshold of NR_CONNS/2. Fixes: b19bc29 ("mptcp: implement delegated actions") Cc: stable@vger.kernel.org Signed-off-by: Vastargazing <vebohr@gmail.com>
1 parent 45436c9 commit 173c10a

2 files changed

Lines changed: 141 additions & 0 deletions

File tree

tools/testing/selftests/net/mptcp/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ TEST_PROGS := \
1414
mptcp_connect_splice.sh \
1515
mptcp_join.sh \
1616
mptcp_sockopt.sh \
17+
mptcp_v6_initcall.sh \
1718
pm_netlink.sh \
1819
simult_flows.sh \
1920
userspace_pm.sh \
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#!/bin/bash
2+
# SPDX-License-Identifier: GPL-2.0
3+
#
4+
# Verify that MPTCP IPv6 subflow child sockets are allocated from the
5+
# TCPv6 slab cache.
6+
#
7+
# tcpv6_prot_override is initialised by copying tcpv6_prot, which is only
8+
# safe after proto_register(&tcpv6_prot) has populated tcpv6_prot.slab.
9+
# If the override copy runs too early during init, override.slab stays
10+
# NULL and __ro_after_init freezes that. Subflow child sockets then fall
11+
# back to kmalloc (kmalloc-4k for a TCPv6 sock), which lacks
12+
# SLAB_TYPESAFE_BY_RCU; lockless ehash lookups in __inet_lookup_established
13+
# can read freed memory.
14+
#
15+
# This test exercises the IPv6 accept path, which goes through the
16+
# override proto, and then asserts that the live TCPv6 slab population
17+
# grew. On a kernel where the override slab is NULL the delta is ~0,
18+
# because the children land in kmalloc-4k instead.
19+
20+
#shellcheck disable=SC2086
21+
22+
. "$(dirname "${0}")/mptcp_lib.sh"
23+
24+
ns1=""
25+
ns2=""
26+
ret=0
27+
28+
NR_CONNS=20
29+
TIMEOUT_POLL=30
30+
TIMEOUT_TEST=$((TIMEOUT_POLL * 2 + 1))
31+
PORT_BASE=20000
32+
33+
# This function is used in the cleanup trap
34+
#shellcheck disable=SC2317,SC2329
35+
cleanup()
36+
{
37+
for ns in "${ns1}" "${ns2}"; do
38+
[ -n "${ns}" ] || continue
39+
ip netns pids "${ns}" | xargs --no-run-if-empty kill -SIGKILL &>/dev/null
40+
done
41+
mptcp_lib_ns_exit "${ns1}" "${ns2}"
42+
}
43+
44+
mptcp_lib_check_mptcp
45+
mptcp_lib_check_tools ip ss
46+
47+
if ! mptcp_lib_kallsyms_has "tcpv6_prot_override$"; then
48+
mptcp_lib_pr_skip "CONFIG_MPTCP_IPV6 not available"
49+
exit ${KSFT_SKIP}
50+
fi
51+
52+
if ! [ -r /proc/slabinfo ]; then
53+
mptcp_lib_pr_skip "/proc/slabinfo not readable"
54+
exit ${KSFT_SKIP}
55+
fi
56+
57+
if ! awk '/^TCPv6 / { found = 1 } END { exit !found }' /proc/slabinfo; then
58+
mptcp_lib_pr_skip "TCPv6 slab cache not present"
59+
exit ${KSFT_SKIP}
60+
fi
61+
62+
trap cleanup EXIT
63+
mptcp_lib_ns_init ns1 ns2
64+
65+
ip -n "${ns1}" link add eth1 type veth peer name eth1 netns "${ns2}"
66+
ip -n "${ns1}" link set eth1 up
67+
ip -n "${ns1}" -6 addr add fc00::1/64 dev eth1 nodad
68+
ip -n "${ns2}" link set eth1 up
69+
ip -n "${ns2}" -6 addr add fc00::2/64 dev eth1 nodad
70+
71+
# Wait for DAD-less addresses to settle
72+
ip -n "${ns1}" -6 route get fc00::2 >/dev/null 2>&1 || sleep 0.1
73+
74+
get_tcpv6_active()
75+
{
76+
awk '/^TCPv6 / { print $2 }' /proc/slabinfo
77+
}
78+
79+
before=$(get_tcpv6_active)
80+
81+
for i in $(seq 1 ${NR_CONNS}); do
82+
echo "a" |
83+
timeout ${TIMEOUT_TEST} \
84+
ip netns exec "${ns2}" \
85+
./mptcp_connect -6 -p $((PORT_BASE + i)) -l \
86+
-t ${TIMEOUT_POLL} -w ${TIMEOUT_POLL} \
87+
:: >/dev/null 2>&1 &
88+
done
89+
90+
# wait_local_port_listen() only checks one port. Walk every port so we
91+
# do not start the connectors before all listeners are ready.
92+
for i in $(seq 1 ${NR_CONNS}); do
93+
mptcp_lib_wait_local_port_listen "${ns2}" $((PORT_BASE + i))
94+
done
95+
96+
for i in $(seq 1 ${NR_CONNS}); do
97+
echo "b" |
98+
timeout ${TIMEOUT_TEST} \
99+
ip netns exec "${ns1}" \
100+
./mptcp_connect -6 -p $((PORT_BASE + i)) \
101+
-t ${TIMEOUT_POLL} -w ${TIMEOUT_POLL} \
102+
fc00::2 >/dev/null 2>&1 &
103+
done
104+
105+
# Wait for the accept side to materialise child sockets. ss reports the
106+
# number of established TCP connections in ns2 owned by mptcp_connect.
107+
established=0
108+
for _ in $(seq 20); do
109+
established=$(ip netns exec "${ns2}" ss -H -t -6 state established 2>/dev/null | wc -l)
110+
[ "${established}" -ge "${NR_CONNS}" ] && break
111+
sleep 1
112+
done
113+
114+
after=$(get_tcpv6_active)
115+
delta=$(( after - before ))
116+
117+
# Conservative threshold: NR_CONNS connections (each producing at least
118+
# a server-side accepted child) must leave a clearly observable footprint
119+
# in the TCPv6 slab. On a regressed kernel, override.slab == NULL routes
120+
# every child into kmalloc-4k and the TCPv6 delta stays near zero.
121+
threshold=$(( NR_CONNS / 2 ))
122+
123+
msg="TCPv6 slab gains MPTCPv6 subflow children"
124+
mptcp_lib_print_title "${msg}"
125+
if [ "${established}" -lt "${NR_CONNS}" ]; then
126+
mptcp_lib_pr_fail "only ${established}/${NR_CONNS} connections established"
127+
mptcp_lib_result_fail "${msg}"
128+
ret=${KSFT_FAIL}
129+
elif [ "${delta}" -ge "${threshold}" ]; then
130+
mptcp_lib_pr_ok "delta=${delta}"
131+
mptcp_lib_result_pass "${msg}"
132+
else
133+
mptcp_lib_pr_fail "delta=${delta} below ${threshold}:" \
134+
"subflow children likely in kmalloc fallback"
135+
mptcp_lib_result_fail "${msg}"
136+
ret=${KSFT_FAIL}
137+
fi
138+
139+
mptcp_lib_result_print_all_tap
140+
exit ${ret}

0 commit comments

Comments
 (0)