* [PATCH net 0/2] net: preserve rps_default_mask on failed writes
@ 2026-10-28 14:52 Yingjie Wang
2026-10-28 14:52 ` [PATCH net 1/2] " Yingjie Wang
2026-10-28 14:52 ` [PATCH net 2/2] selftests: net: check failed rps_default_mask writes Yingjie Wang
0 siblings, 2 replies; 5+ messages in thread
From: Yingjie Wang @ 2026-10-28 14:52 UTC (permalink / raw)
To: netdev@vger.kernel.org
Cc: Yingjie Wang, davem@davemloft.net, edumazet@kernel.org,
kuba@kernel.org, pabeni@redhat.com, horms@kernel.org,
shuah@kernel.org, linux-kselftest@vger.kernel.org,
linux-kernel@vger.kernel.org
rps_default_mask_sysctl() parses and validates writes in the live
per-netns mask. Both the bitmap parser and housekeeping validation can
modify that mask before returning an error, so a failed write can change
the default applied to RX queues created later.
Parse and validate a temporary mask before publishing it, and add a
regression test for a partially parsed invalid value.
On the base kernel, the new test fails because the mask changes from 1
to 3. With the fix, all ten rps_default_mask selftests pass. Tests with
isolated CPUs also confirm that overflow and housekeeping failures leave
the mask unchanged, while valid writes continue to work.
Yingjie Wang (2):
net: preserve rps_default_mask on failed writes
selftests: net: check failed rps_default_mask writes
net/core/sysctl_net_core.c | 23 +++++++++++--------
.../testing/selftests/net/rps_default_mask.sh | 23 +++++++++++++++++++
2 files changed, 36 insertions(+), 10 deletions(-)
base-commit: 94cea342e8915812b45f93c56e134041c7fdb4d7
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net 1/2] net: preserve rps_default_mask on failed writes
2026-10-28 14:52 [PATCH net 0/2] net: preserve rps_default_mask on failed writes Yingjie Wang
@ 2026-10-28 14:52 ` Yingjie Wang
2026-10-28 16:54 ` Wang Zhan
2026-10-29 3:10 ` Wang Zhan
2026-10-28 14:52 ` [PATCH net 2/2] selftests: net: check failed rps_default_mask writes Yingjie Wang
1 sibling, 2 replies; 5+ messages in thread
From: Yingjie Wang @ 2026-10-28 14:52 UTC (permalink / raw)
To: netdev@vger.kernel.org
Cc: Yingjie Wang, davem@davemloft.net, edumazet@kernel.org,
kuba@kernel.org, pabeni@redhat.com, horms@kernel.org,
shuah@kernel.org, linux-kselftest@vger.kernel.org,
linux-kernel@vger.kernel.org
rps_default_mask_sysctl() passes the live per-netns mask to
cpumask_parse(). bitmap_parse() stores chunks as it scans from right to
left, so a later error can leave lower chunks changed.
rps_cpumask_housekeeping() also intersects its input in place before
returning -EINVAL when no housekeeping CPUs remain.
Consequently, a failed write changes both the value read from the sysctl
and the default copied to RX queues created later. On a four-CPU VM,
after setting the mask to 1, writing zz,00000003 failed with EOVERFLOW
but changed the mask to 3. Writing a mask containing only isolated CPUs
failed with EINVAL but changed it to 0. No kernel modification or error
injection was needed to reproduce either case.
Parse and validate a temporary mask, then replace the live pointer only
after both steps succeed. Readers access and consume the pointer while
holding the same mutex, so the replaced mask can be freed after dropping
the mutex.
The rps_default_mask selftest failed on the base kernel and passed with
the fix. The fixed kernel also kept the mask at 1 for the overflow and
housekeeping failure cases, while valid writes of 2 and 0 still
succeeded.
The issue was found while fuzzing sysctl write error paths.
Fixes: 605cfa1b1090 ("net: introduce default_rps_mask netns attribute")
Signed-off-by: Yingjie Wang <x-professor@outlook.com>
---
net/core/sysctl_net_core.c | 23 +++++++++++++----------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index 473c162736d69..6abd77ef92c86 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -105,27 +105,29 @@ static int rps_default_mask_sysctl(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
struct net *net = (struct net *)table->data;
+ struct cpumask *new_mask = NULL;
struct cpumask *mask;
int err = 0;
+ if (write) {
+ new_mask = kzalloc(cpumask_size(), GFP_KERNEL);
+ if (!new_mask)
+ return -ENOMEM;
+ }
+
mutex_lock(&rps_default_mask_mutex);
mask = net->core.rps_default_mask;
if (write) {
- if (!mask) {
- mask = kzalloc(cpumask_size(), GFP_KERNEL);
- net->core.rps_default_mask = mask;
- }
- err = -ENOMEM;
- if (!mask)
- goto done;
-
- err = cpumask_parse(buffer, mask);
+ err = cpumask_parse(buffer, new_mask);
if (err)
goto done;
- err = rps_cpumask_housekeeping(mask);
+ err = rps_cpumask_housekeeping(new_mask);
if (err)
goto done;
+
+ net->core.rps_default_mask = new_mask;
+ new_mask = mask;
} else {
err = dump_cpumask(buffer, lenp, ppos,
mask ?: cpu_none_mask);
@@ -133,6 +135,7 @@ static int rps_default_mask_sysctl(const struct ctl_table *table, int write,
done:
mutex_unlock(&rps_default_mask_mutex);
+ kfree(new_mask);
return err;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH net 2/2] selftests: net: check failed rps_default_mask writes
2026-10-28 14:52 [PATCH net 0/2] net: preserve rps_default_mask on failed writes Yingjie Wang
2026-10-28 14:52 ` [PATCH net 1/2] " Yingjie Wang
@ 2026-10-28 14:52 ` Yingjie Wang
1 sibling, 0 replies; 5+ messages in thread
From: Yingjie Wang @ 2026-10-28 14:52 UTC (permalink / raw)
To: netdev@vger.kernel.org
Cc: Yingjie Wang, davem@davemloft.net, edumazet@kernel.org,
kuba@kernel.org, pabeni@redhat.com, horms@kernel.org,
shuah@kernel.org, linux-kselftest@vger.kernel.org,
linux-kernel@vger.kernel.org
A failed write to rps_default_mask must not change the configured value.
Add a case with a valid low chunk followed by an invalid high chunk. The
old handler leaves the low chunk installed before returning an error,
whereas the fixed handler retains the previous mask.
Signed-off-by: Yingjie Wang <x-professor@outlook.com>
---
.../testing/selftests/net/rps_default_mask.sh | 23 +++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/tools/testing/selftests/net/rps_default_mask.sh b/tools/testing/selftests/net/rps_default_mask.sh
index b200019b3c80a..91e163fd25450 100755
--- a/tools/testing/selftests/net/rps_default_mask.sh
+++ b/tools/testing/selftests/net/rps_default_mask.sh
@@ -45,8 +45,31 @@ chk_rps() {
fi
}
+chk_rps_default_mask() {
+ local rps_mask expected_rps_mask=$2
+ local msg=$1
+
+ rps_mask=$(cat /proc/sys/net/core/rps_default_mask)
+ printf "%-60s" "$msg"
+
+ rps_mask=${rps_mask//,}
+ if [ "$rps_mask" -eq "$expected_rps_mask" ]; then
+ echo "[ ok ]"
+ else
+ echo "[fail] expected $expected_rps_mask found $rps_mask"
+ ret=1
+ fi
+}
+
trap cleanup EXIT
+echo 1 > /proc/sys/net/core/rps_default_mask
+if echo "zz,00000003" > /proc/sys/net/core/rps_default_mask 2>/dev/null; then
+ echo "invalid rps_default_mask write unexpectedly succeeded"
+ ret=1
+fi
+chk_rps_default_mask "failed write doesn't change rps_default_mask" 1
+
echo 0 > /proc/sys/net/core/rps_default_mask
setup
chk_rps "empty rps_default_mask" $NETNS lo 0
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH net 1/2] net: preserve rps_default_mask on failed writes
2026-10-28 14:52 ` [PATCH net 1/2] " Yingjie Wang
@ 2026-10-28 16:54 ` Wang Zhan
2026-10-29 3:10 ` Wang Zhan
1 sibling, 0 replies; 5+ messages in thread
From: Wang Zhan @ 2026-10-28 16:54 UTC (permalink / raw)
To: netdev, Yingjie Wang
Cc: Wang Zhan, davem, edumazet, kuba, pabeni, horms, shuah,
linux-kselftest, linux-kernel
On Wed, 28 Oct 2026 14:52:09 +0000 Yingjie Wang wrote:
> rps_default_mask_sysctl() passes the live per-netns mask to
> cpumask_parse(). bitmap_parse() stores chunks as it scans from right to
> left, so a later error can leave lower chunks changed.
> rps_cpumask_housekeeping() also intersects its input in place before
> returning -EINVAL when no housekeeping CPUs remain.
>
> Consequently, a failed write changes both the value read from the sysctl
> and the default copied to RX queues created later. On a four-CPU VM,
> after setting the mask to 1, writing zz,00000003 failed with EOVERFLOW
> but changed the mask to 3. Writing a mask containing only isolated CPUs
> failed with EINVAL but changed it to 0. No kernel modification or error
> injection was needed to reproduce either case.
>
> Parse and validate a temporary mask, then replace the live pointer only
> after both steps succeed. Readers access and consume the pointer while
> holding the same mutex, so the replaced mask can be freed after dropping
> the mutex.
>
> The rps_default_mask selftest failed on the base kernel and passed with
> the fix. The fixed kernel also kept the mask at 1 for the overflow and
> housekeeping failure cases, while valid writes of 2 and 0 still
> succeeded.
>
> The issue was found while fuzzing sysctl write error paths.
>
> Fixes: 605cfa1b1090 ("net: introduce default_rps_mask netns attribute")
> Signed-off-by: Yingjie Wang <x-professor@outlook.com>
Reviewed-by: Wang Zhan <wang.zhan@smartx.com>
Tested-by: Wang Zhan <wang.zhan@smartx.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH net 1/2] net: preserve rps_default_mask on failed writes
2026-10-28 14:52 ` [PATCH net 1/2] " Yingjie Wang
2026-10-28 16:54 ` Wang Zhan
@ 2026-10-29 3:10 ` Wang Zhan
1 sibling, 0 replies; 5+ messages in thread
From: Wang Zhan @ 2026-10-29 3:10 UTC (permalink / raw)
To: netdev, Yingjie Wang
Cc: Wang Zhan, davem, edumazet, kuba, pabeni, horms, shuah,
linux-kselftest, linux-kernel
On Wed, 28 Oct 2026 14:52:09 +0000 Yingjie Wang wrote:
> @@ -105,27 +105,29 @@ static int rps_default_mask_sysctl(const struct ctl_table *table, int write,
> void *buffer, size_t *lenp, loff_t *ppos)
> {
> struct net *net = (struct net *)table->data;
> + struct cpumask *new_mask = NULL;
> struct cpumask *mask;
> int err = 0;
>
> + if (write) {
> + new_mask = kzalloc(cpumask_size(), GFP_KERNEL);
> + if (!new_mask)
> + return -ENOMEM;
> + }
> +
> mutex_lock(&rps_default_mask_mutex);
> mask = net->core.rps_default_mask;
> if (write) {
> - if (!mask) {
> - mask = kzalloc(cpumask_size(), GFP_KERNEL);
> - net->core.rps_default_mask = mask;
> - }
> - err = -ENOMEM;
> - if (!mask)
> - goto done;
> -
> - err = cpumask_parse(buffer, mask);
> + err = cpumask_parse(buffer, new_mask);
> if (err)
> goto done;
>
> - err = rps_cpumask_housekeeping(mask);
> + err = rps_cpumask_housekeeping(new_mask);
> + if (err)
> + goto done;
Please consider parsing and validating new_mask before taking
rps_default_mask_mutex. It remains private to this handler until published.
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-29 3:11 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-28 14:52 [PATCH net 0/2] net: preserve rps_default_mask on failed writes Yingjie Wang
2026-10-28 14:52 ` [PATCH net 1/2] " Yingjie Wang
2026-10-28 16:54 ` Wang Zhan
2026-10-29 3:10 ` Wang Zhan
2026-10-28 14:52 ` [PATCH net 2/2] selftests: net: check failed rps_default_mask writes Yingjie Wang
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.