* [Devel] [PATCH hci-8.1 0/2] mempolicy: prohibit mbind and set_mempolicy_home_node in VE
@ 2026-11-05 18:48 Konstantin Khorenko
2026-11-05 18:48 ` [Devel] [PATCH hci-8.1 1/2] " Konstantin Khorenko
2026-11-05 18:48 ` [Devel] [PATCH hci-8.1 2/2] selftests/ve: check that the NUMA API syscalls return ENOSYS in a VE Konstantin Khorenko
0 siblings, 2 replies; 5+ messages in thread
From: Konstantin Khorenko @ 2026-11-05 18:48 UTC (permalink / raw)
To: Pavel Tikhomirov, Vasileios Almpanis; +Cc: devel
A CT is meant to see a kernel without NUMA, so the NUMA API syscalls
return -ENOSYS in a non-super VE.
mbind(2) was blocked in vz7, but the vz8 port put its check into the
compat entry point only, and the compat entry points were dropped on
the rebase to RHEL9. Since then a CT task can bind its pages to nodes
and migrate them with mbind(2), while get_mempolicy(2) tells it there
is no NUMA.
set_mempolicy_home_node(2) appeared after vz7 and was never blocked. It
only sets the home node of a policy installed by mbind(2), so it goes
with it.
- Patch 1 returns -ENOSYS from both in a non-super VE.
- Patch 2 adds a ve selftest: all six NUMA API syscalls, with arguments
that succeed on the host, must return -ENOSYS in a VE.
The unmerged selftests series for VSTOR-137234 carries an older
ve_numa_syscalls_prohibited_test that covers four of the syscalls with
arguments that fail on the host anyway; this version replaces it.
Tested on a 4-node host, ve_numa_syscalls_prohibited_test:
+----------------------------+--------------+--------------------------+
| kernel | host calls | calls in a VE |
+----------------------------+--------------+--------------------------+
| vz10 without patch 1 | PASS | FAIL: mbind and |
| | | set_mempolicy_home_node |
| | | return 0 |
+----------------------------+--------------+--------------------------+
| vz10 with the series | PASS | PASS |
+----------------------------+--------------+--------------------------+
ve_numa_migrate_test still passes with the series.
Assisted-by: LLM
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
Konstantin Khorenko (2):
mempolicy: prohibit mbind and set_mempolicy_home_node in VE
selftests/ve: check that the NUMA API syscalls return ENOSYS in a VE
mm/mempolicy.c | 6 +
tools/testing/selftests/ve/.gitignore | 1 +
tools/testing/selftests/ve/Makefile | 1 +
.../ve/ve_numa_syscalls_prohibited_test.c | 192 +++++++++++++++++++++
4 files changed, 200 insertions(+)
---
base-commit: 1f21db982845109040d1feb279e9ee34f8e6f065
change-id: 20261105-b4-prep-khorenko-vstor-153679-mbind-ve-bd811ea9be42
Best regards,
--
Konstantin Khorenko,
Virtuozzo Linux Kernel Team
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 5+ messages in thread
* [Devel] [PATCH hci-8.1 1/2] mempolicy: prohibit mbind and set_mempolicy_home_node in VE
2026-11-05 18:48 [Devel] [PATCH hci-8.1 0/2] mempolicy: prohibit mbind and set_mempolicy_home_node in VE Konstantin Khorenko
@ 2026-11-05 18:48 ` Konstantin Khorenko
2026-11-05 18:52 ` no-reply
2026-11-05 18:48 ` [Devel] [PATCH hci-8.1 2/2] selftests/ve: check that the NUMA API syscalls return ENOSYS in a VE Konstantin Khorenko
1 sibling, 1 reply; 5+ messages in thread
From: Konstantin Khorenko @ 2026-11-05 18:48 UTC (permalink / raw)
To: Pavel Tikhomirov, Vasileios Almpanis; +Cc: devel
A CT is meant to see a kernel without NUMA support: in a non-super VE
the NUMA API syscalls return -ENOSYS, as they do in a kernel built
without CONFIG_NUMA.
Return -ENOSYS from mbind(2) and set_mempolicy_home_node(2) at the
start of the handler, before any argument check, as the other NUMA API
syscalls do.
Fixes: a803a5b2ea97 ("mempolicy: prohibit all NUMA API syscalls in VE")
https://virtuozzo.atlassian.net/browse/VSTOR-153679
Feature: mm: prohibit NUMA API inside CT
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
mm/mempolicy.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 3255d3e987180..9b2a32ef92949 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -1584,6 +1584,9 @@ SYSCALL_DEFINE4(set_mempolicy_home_node, unsigned long, start, unsigned long, le
int err = -ENOENT;
VMA_ITERATOR(vmi, mm, start);
+ if (!ve_is_super(get_exec_env()))
+ return -ENOSYS;
+
start = untagged_addr(start);
if (start & ~PAGE_MASK)
return -EINVAL;
@@ -1645,6 +1648,9 @@ SYSCALL_DEFINE6(mbind, unsigned long, start, unsigned long, len,
unsigned long, mode, const unsigned long __user *, nmask,
unsigned long, maxnode, unsigned int, flags)
{
+ if (!ve_is_super(get_exec_env()))
+ return -ENOSYS;
+
return kernel_mbind(start, len, mode, nmask, maxnode, flags);
}
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 5+ messages in thread
* [Devel] [PATCH hci-8.1 2/2] selftests/ve: check that the NUMA API syscalls return ENOSYS in a VE
2026-11-05 18:48 [Devel] [PATCH hci-8.1 0/2] mempolicy: prohibit mbind and set_mempolicy_home_node in VE Konstantin Khorenko
2026-11-05 18:48 ` [Devel] [PATCH hci-8.1 1/2] " Konstantin Khorenko
@ 2026-11-05 18:48 ` Konstantin Khorenko
2026-11-05 18:54 ` no-reply
1 sibling, 1 reply; 5+ messages in thread
From: Konstantin Khorenko @ 2026-11-05 18:48 UTC (permalink / raw)
To: Pavel Tikhomirov, Vasileios Almpanis; +Cc: devel
A CT must see a kernel without NUMA, so all six NUMA API syscalls
return -ENOSYS in a non-super VE.
The test makes each call with arguments that succeed on the host and
checks that the host gets 0, while a task in a VE gets -ENOSYS. The
host result shows that the ENOSYS can only come from the VE check, not
from a bad argument.
https://virtuozzo.atlassian.net/browse/VSTOR-153679
Feature: mm: prohibit NUMA API inside CT
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
tools/testing/selftests/ve/.gitignore | 1 +
tools/testing/selftests/ve/Makefile | 1 +
.../ve/ve_numa_syscalls_prohibited_test.c | 192 +++++++++++++++++++++
3 files changed, 194 insertions(+)
diff --git a/tools/testing/selftests/ve/.gitignore b/tools/testing/selftests/ve/.gitignore
index 3d1179bd2c5f4..5e07985c8fed3 100644
--- a/tools/testing/selftests/ve/.gitignore
+++ b/tools/testing/selftests/ve/.gitignore
@@ -4,3 +4,4 @@ ve_mount_accounting_test
ve_cpuid_override_test
ve_vtty_stress
ve_numa_migrate_test
+ve_numa_syscalls_prohibited_test
diff --git a/tools/testing/selftests/ve/Makefile b/tools/testing/selftests/ve/Makefile
index e96320a183844..451a7e1cc17d1 100644
--- a/tools/testing/selftests/ve/Makefile
+++ b/tools/testing/selftests/ve/Makefile
@@ -7,6 +7,7 @@ TEST_GEN_PROGS += ve_perms_test
TEST_GEN_PROGS += ve_mount_accounting_test
TEST_GEN_PROGS += ve_vtty_stress
TEST_GEN_PROGS += ve_numa_migrate_test
+TEST_GEN_PROGS += ve_numa_syscalls_prohibited_test
# CPUID faulting is x86-64 only. ARCH says "x86" for 32-bit too and lib.mk
# picks the clang target by its emptiness, so decide by the compiler.
diff --git a/tools/testing/selftests/ve/ve_numa_syscalls_prohibited_test.c b/tools/testing/selftests/ve/ve_numa_syscalls_prohibited_test.c
new file mode 100644
index 0000000000000..4cae1c74b46bc
--- /dev/null
+++ b/tools/testing/selftests/ve/ve_numa_syscalls_prohibited_test.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Every NUMA API syscall works on the host and returns ENOSYS in a VE. */
+#define _GNU_SOURCE
+#include <errno.h>
+#include <sched.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <linux/mempolicy.h>
+
+#include "../kselftest_harness.h"
+#include "ve_selftest.h"
+
+#define NODE_BITS 1024
+#define LONG_BITS (8 * sizeof(unsigned long))
+
+static unsigned long node_mask[NODE_BITS / LONG_BITS];
+static void *page;
+static size_t page_size;
+
+static long call_get_mempolicy(void)
+{
+ int mode;
+
+ return syscall(__NR_get_mempolicy, &mode, NULL, 0UL, 0UL, 0UL);
+}
+
+static long call_set_mempolicy(void)
+{
+ return syscall(__NR_set_mempolicy, MPOL_DEFAULT, NULL, 0UL);
+}
+
+static long call_mbind(void)
+{
+ return syscall(__NR_mbind, page, page_size, MPOL_BIND, node_mask,
+ NODE_BITS + 1UL, 0U);
+}
+
+static long call_set_mempolicy_home_node(void)
+{
+ unsigned long node;
+
+ for (node = 0; node < NODE_BITS; node++)
+ if (node_mask[node / LONG_BITS] & (1UL << (node % LONG_BITS)))
+ break;
+ return syscall(__NR_set_mempolicy_home_node, page, page_size, node, 0UL);
+}
+
+static long call_migrate_pages(void)
+{
+ unsigned long none[NODE_BITS / LONG_BITS] = { 0 };
+
+ return syscall(__NR_migrate_pages, 0, NODE_BITS + 1UL, none, node_mask);
+}
+
+static long call_move_pages(void)
+{
+ return syscall(__NR_move_pages, 0, 0UL, NULL, NULL, NULL, 0);
+}
+
+/* set_mempolicy_home_node() needs the MPOL_BIND policy mbind() installs. */
+static const struct numa_call {
+ const char *name;
+ long (*call)(void);
+} numa_calls[] = {
+ { "get_mempolicy", call_get_mempolicy },
+ { "set_mempolicy", call_set_mempolicy },
+ { "mbind", call_mbind },
+ { "set_mempolicy_home_node", call_set_mempolicy_home_node },
+ { "migrate_pages", call_migrate_pages },
+ { "move_pages", call_move_pages },
+};
+
+#define NR_CALLS ARRAY_SIZE(numa_calls)
+
+struct call_result {
+ long ret;
+ int err;
+};
+
+static void run_calls(struct call_result *res)
+{
+ unsigned int i;
+
+ for (i = 0; i < NR_CALLS; i++) {
+ errno = 0;
+ res[i].ret = numa_calls[i].call();
+ res[i].err = errno;
+ }
+}
+
+FIXTURE(numa_syscalls)
+{
+ int cgv2_fd;
+ int ctid;
+};
+
+FIXTURE_SETUP(numa_syscalls)
+{
+ unsigned long allowed[NODE_BITS / LONG_BITS] = { 0 };
+ unsigned int i;
+
+ self->cgv2_fd = -1;
+
+ if (geteuid() != 0)
+ SKIP(return, "creating a ve cgroup needs root");
+
+ page_size = getpagesize();
+ page = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ ASSERT_NE(page, MAP_FAILED);
+
+ ASSERT_EQ(syscall(__NR_get_mempolicy, NULL, allowed, NODE_BITS + 1UL,
+ 0UL, MPOL_F_MEMS_ALLOWED), 0);
+ for (i = 0; i < NODE_BITS; i++) {
+ if (allowed[i / LONG_BITS] & (1UL << (i % LONG_BITS))) {
+ node_mask[i / LONG_BITS] = 1UL << (i % LONG_BITS);
+ break;
+ }
+ }
+ ASSERT_LT(i, NODE_BITS);
+
+ self->cgv2_fd = mount_cg2_fd();
+ ASSERT_GE(self->cgv2_fd, 0);
+ ASSERT_EQ(write_file_at(self->cgv2_fd, "cgroup.subtree_control",
+ VE_CONTROLLERS), 0);
+
+ self->ctid = make_ve(self->cgv2_fd, CTID_MIN);
+ ASSERT_GE(self->ctid, 0);
+}
+
+FIXTURE_TEARDOWN(numa_syscalls)
+{
+ if (self->cgv2_fd >= 0) {
+ destroy_ve(self->cgv2_fd, self->ctid);
+ close(self->cgv2_fd);
+ }
+}
+
+TEST_F(numa_syscalls, host_calls_succeed)
+{
+ struct call_result res[NR_CALLS];
+ unsigned int i;
+
+ run_calls(res);
+ for (i = 0; i < NR_CALLS; i++) {
+ EXPECT_EQ(res[i].ret, 0)
+ TH_LOG("%s on the host: %s", numa_calls[i].name,
+ strerror(res[i].err));
+ }
+}
+
+TEST_F(numa_syscalls, ve_calls_return_enosys)
+{
+ struct call_result res[NR_CALLS];
+ int pipefd[2], status;
+ unsigned int i;
+ pid_t pid;
+
+ ASSERT_EQ(pipe(pipefd), 0);
+ pid = fork();
+ ASSERT_GE(pid, 0);
+ if (pid == 0) {
+ close(pipefd[0]);
+ if (enter_cgroup(self->cgv2_fd, self->ctid) < 0)
+ _exit(1);
+ if (unshare(CLONE_NEWVE) < 0)
+ _exit(2);
+ run_calls(res);
+ if (write(pipefd[1], res, sizeof(res)) != sizeof(res))
+ _exit(3);
+ _exit(0);
+ }
+ close(pipefd[1]);
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+ ASSERT_TRUE(WIFEXITED(status));
+ ASSERT_EQ(WEXITSTATUS(status), 0)
+ TH_LOG("VE child setup failed at step %d", WEXITSTATUS(status));
+ ASSERT_EQ(read(pipefd[0], res, sizeof(res)), sizeof(res));
+ close(pipefd[0]);
+
+ for (i = 0; i < NR_CALLS; i++) {
+ EXPECT_TRUE(res[i].ret == -1 && res[i].err == ENOSYS)
+ TH_LOG("%s in a VE: returned %ld (%s), expected ENOSYS",
+ numa_calls[i].name, res[i].ret,
+ strerror(res[i].err));
+ }
+}
+
+TEST_HARNESS_MAIN
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [Devel] [PATCH hci-8.1 1/2] mempolicy: prohibit mbind and set_mempolicy_home_node in VE
2026-11-05 18:48 ` [Devel] [PATCH hci-8.1 1/2] " Konstantin Khorenko
@ 2026-11-05 18:52 ` no-reply
0 siblings, 0 replies; 5+ messages in thread
From: no-reply @ 2026-11-05 18:52 UTC (permalink / raw)
To: Konstantin Khorenko, Pavel Tikhomirov, Vasileios Almpanis; +Cc: devel
> A CT is meant to see a kernel without NUMA support: in a non-super VE
> the NUMA API syscalls return -ENOSYS, as they do in a kernel built
> without CONFIG_NUMA.
>
> Return -ENOSYS from mbind(2) and set_mempolicy_home_node(2) at the
> start of the handler, before any argument check, as the other NUMA API
> syscalls do.
>
> Fixes: a803a5b2ea97 ("mempolicy: prohibit all NUMA API syscalls in VE")
> https://virtuozzo.atlassian.net/browse/VSTOR-153679
>
> Feature: mm: prohibit NUMA API inside CT
> Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261105-b4-prep-khorenko-vstor-153679-mbind-ve-v1-0-b4fcd27c8001@virtuozzo.com?part=1
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [Devel] [PATCH hci-8.1 2/2] selftests/ve: check that the NUMA API syscalls return ENOSYS in a VE
2026-11-05 18:48 ` [Devel] [PATCH hci-8.1 2/2] selftests/ve: check that the NUMA API syscalls return ENOSYS in a VE Konstantin Khorenko
@ 2026-11-05 18:54 ` no-reply
0 siblings, 0 replies; 5+ messages in thread
From: no-reply @ 2026-11-05 18:54 UTC (permalink / raw)
To: Konstantin Khorenko, Pavel Tikhomirov, Vasileios Almpanis; +Cc: devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] selftests/ve: numa syscalls test fails instead of skipping on non-NUMA host
- [Low] selftests/ve: FIXTURE_SETUP does not initialise ctid before teardown uses it
--
commit 3833f07c24efbbde914f6df791979fabf50e44b6
Author: Konstantin Khorenko <khorenko@virtuozzo.com>
selftests/ve: check that the NUMA API syscalls return ENOSYS in a VE
This adds a selftest that calls all six NUMA API syscalls on the host and
inside a VE. It expects 0 on the host and -ENOSYS in the VE.
Link: https://virtuozzo.atlassian.net/browse/VSTOR-153679
> diff --git a/tools/testing/selftests/ve/ve_numa_syscalls_prohibited_test.c b/tools/testing/selftests/ve/ve_numa_syscalls_prohibited_test.c
> new file mode 100644
> index 000000000000..4cae1c74b46b
> --- /dev/null
> +++ b/tools/testing/selftests/ve/ve_numa_syscalls_prohibited_test.c
[ ... ]
> +FIXTURE(numa_syscalls)
> +{
> + int cgv2_fd;
> + int ctid;
> +};
> +
> +FIXTURE_SETUP(numa_syscalls)
> +{
> + unsigned long allowed[NODE_BITS / LONG_BITS] = { 0 };
> + unsigned int i;
> +
> + self->cgv2_fd = -1;
> +
> + if (geteuid() != 0)
> + SKIP(return, "creating a ve cgroup needs root");
[ ... ]
> + ASSERT_EQ(syscall(__NR_get_mempolicy, NULL, allowed, NODE_BITS + 1UL,
> + 0UL, MPOL_F_MEMS_ALLOWED), 0);
[Severity: Medium]
Can this probe turn into a false failure on a host without NUMA support?
If the host kernel lacks CONFIG_NUMA or CONFIG_MIGRATION, or seccomp blocks
these syscalls, get_mempolicy() returns -ENOSYS on the host. The
ASSERT_EQ(..., 0) in FIXTURE_SETUP(numa_syscalls) then aborts setup with a
failure, and ve_calls_return_enosys never runs.
The sibling ve_numa_migrate_test uses SKIP for missing NUMA preconditions.
Would a SKIP be more appropriate here when the probe returns -1 with
errno == ENOSYS?
[ ... ]
> +FIXTURE_TEARDOWN(numa_syscalls)
> +{
> + if (self->cgv2_fd >= 0) {
> + destroy_ve(self->cgv2_fd, self->ctid);
> + close(self->cgv2_fd);
> + }
> +}
[Severity: Low]
Is self->ctid initialized before FIXTURE_TEARDOWN(numa_syscalls) uses it?
FIXTURE_SETUP(numa_syscalls) sets self->cgv2_fd = -1 but never sets
self->ctid. If an ASSERT fails after mount_cg2_fd() succeeds and before
make_ve() assigns ctid, for example the write_file_at() of
cgroup.subtree_control, the teardown sees cgv2_fd >= 0 and calls
destroy_ve(self->cgv2_fd, self->ctid) with ctid unset.
destroy_ve() only returns early for id < 0, so it would try to remove
cgroup "0" with its retry loop. Would setting self->ctid = -1 next to
self->cgv2_fd = -1 avoid this?
> +TEST_F(numa_syscalls, host_calls_succeed)
> +{
> + struct call_result res[NR_CALLS];
> + unsigned int i;
> +
> + run_calls(res);
> + for (i = 0; i < NR_CALLS; i++) {
> + EXPECT_EQ(res[i].ret, 0)
> + TH_LOG("%s on the host: %s", numa_calls[i].name,
> + strerror(res[i].err));
> + }
> +}
[Severity: Medium]
Does host_calls_succeed report a failure when a call returns -ENOSYS on the
host?
On a host where the NUMA syscalls are unavailable, the EXPECT_EQ(res[i].ret,
0) check fails the test instead of skipping it. The commit message says the
host result is what shows the ENOSYS in the VE comes from the VE check, so
an environment that cannot provide that baseline might be better reported
as a skip.
--
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261105-b4-prep-khorenko-vstor-153679-mbind-ve-v1-0-b4fcd27c8001@virtuozzo.com?part=2
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-05 18:55 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-05 18:48 [Devel] [PATCH hci-8.1 0/2] mempolicy: prohibit mbind and set_mempolicy_home_node in VE Konstantin Khorenko
2026-11-05 18:48 ` [Devel] [PATCH hci-8.1 1/2] " Konstantin Khorenko
2026-11-05 18:52 ` no-reply
2026-11-05 18:48 ` [Devel] [PATCH hci-8.1 2/2] selftests/ve: check that the NUMA API syscalls return ENOSYS in a VE Konstantin Khorenko
2026-11-05 18:54 ` no-reply
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.