From: Konstantin Khorenko <khorenko@virtuozzo.com>
To: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>,
Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>,
Vasileios Almpanis <vasileios.almpanis@virtuozzo.com>
Cc: devel@openvz.org
Subject: [Devel] [PATCH hci-8.1 1/7] selftests/mm: cover the MADV_DOEXEC interface and VMA attributes
Date: Wed, 14 Oct 2026 19:14:59 +0200 [thread overview]
Message-ID: <20261014-b4-prep-khorenko-vstor-149847-5-doexec-selftests-v1-1-b23b454691db@virtuozzo.com> (raw)
In-Reply-To: <20261014-b4-prep-khorenko-vstor-149847-5-doexec-selftests-v1-0-b23b454691db@virtuozzo.com>
Add cases for what madvise accepts and what the copy of a preserved VMA
keeps:
- /proc/sys/vm/madv_doexec_flag reports MADV_DOEXEC, and a kernel
without the sysctl does not accept MADV_DOEXEC either.
- Shared anonymous, private file, executable, MAP_GROWSDOWN and stack
mappings are refused with EINVAL, and so is a hugetlb mapping when
huge pages are available.
- A range unmarked with MADV_DONTEXEC (twice, the second a no-op) is
not preserved.
- Marking the middle page of a mapping preserves that page only.
- An mlocked range is preserved without VM_LOCKED.
- MADV_DONTFORK and MADV_WIPEONFORK carry over, so a fork in the new
image leaves the range out or wipes it.
- A userfaultfd registration does not carry over.
- The memory policy of the range carries over.
A flag expected to disappear is checked in smaps before the exec as
well, so that a failed lookup cannot pass for its absence. EINVAL from
MADV_DOEXEC fails a case on a 64-bit kernel with the madv_doexec_flag
sysctl, in the existing cases too; the cases skip only when something
they need is missing: the feature, free huge pages, the mlock limit,
userfaultfd, mbind. A failing case prints what failed, and the
children running the cases, old and new, exit with exit(), so that the
message reaches a pipe.
New cases go to a table, and the checks run in the new image to a
second one, so that main() stays small.
https://virtuozzo.atlassian.net/browse/VSTOR-150234
Feature: oracle/mm: MADV_DOEXEC madvise() flag
Assisted-by: LLM
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
tools/testing/selftests/mm/doexec.c | 424 +++++++++++++++++++++++++++++++++---
1 file changed, 392 insertions(+), 32 deletions(-)
diff --git a/tools/testing/selftests/mm/doexec.c b/tools/testing/selftests/mm/doexec.c
index a89fe2f067df5..850b498e3d1dc 100644
--- a/tools/testing/selftests/mm/doexec.c
+++ b/tools/testing/selftests/mm/doexec.c
@@ -1,10 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Verify that exec preserves the memory backing long-term DMA pins. */
+/* Verify what MADV_DOEXEC preserves across exec, long-term DMA pins first. */
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <limits.h>
+#include <linux/mempolicy.h>
+#include <linux/userfaultfd.h>
#include <linux/vfio.h>
#include <linux/vhost.h>
#include <poll.h>
@@ -25,12 +27,16 @@
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/sysmacros.h>
+#include <sys/utsname.h>
#include <sys/wait.h>
#include <unistd.h>
#include "../../../../mm/gup_test.h"
#include "../kselftest.h"
+#ifndef UFFD_USER_MODE_ONLY
+#define UFFD_USER_MODE_ONLY 1
+#endif
#ifndef MADV_DOEXEC
#define MADV_DOEXEC 201
#endif
@@ -40,6 +46,9 @@
#ifndef MADV_COLLAPSE
#define MADV_COLLAPSE 25
#endif
+#ifndef MADV_WIPEONFORK
+#define MADV_WIPEONFORK 18
+#endif
/* The downstream ELF loader requires this opt-in in a PT_NOTE segment. */
asm(".pushsection .note.doexec,\"a\",%note\n"
@@ -117,6 +126,38 @@ static int check_bytes(char *ram, size_t size, unsigned char value)
return KSFT_PASS;
}
+/*
+ * EINVAL from MADV_DOEXEC means no feature only on a kernel without the
+ * sysctl, or on a 32-bit one, which has no VM_EXEC_KEEP.
+ */
+static int unsupported(void)
+{
+ int err = errno;
+ struct utsname uts;
+
+ if (err == EINVAL &&
+ (access("/proc/sys/vm/madv_doexec_flag", F_OK) ||
+ (!uname(&uts) && !strstr(uts.machine, "64") &&
+ strcmp(uts.machine, "s390x") && strcmp(uts.machine, "alpha"))))
+ return KSFT_SKIP;
+ ksft_print_msg("MADV_DOEXEC: %s\n", strerror(err));
+ fflush(stdout);
+ errno = err;
+ return KSFT_FAIL;
+}
+
+static int fail(const char *what)
+{
+ ksft_print_msg("%s\n", what);
+ fflush(stdout);
+ return KSFT_FAIL;
+}
+
+static int check_contents(char *ram, size_t size, unsigned char value)
+{
+ return check_bytes(ram, size, value) ? fail("contents lost") : KSFT_PASS;
+}
+
static int after_cow_exec(char *ram, size_t size, pid_t sibling, int pipe_fd)
{
char ready = 1;
@@ -146,7 +187,7 @@ static int before_cow_exec(void)
return KSFT_FAIL;
memset(ram, 0x31, size);
if (madvise(ram, size, MADV_DOEXEC))
- return errno == EINVAL ? KSFT_SKIP : KSFT_FAIL;
+ return unsupported();
if (pipe(pipe_fd))
return KSFT_FAIL;
pid = fork();
@@ -272,10 +313,8 @@ static int before_exec(int kind)
return KSFT_SKIP;
if (kind == LOCKED && mlock(ram, size))
return KSFT_SKIP;
- if (madvise(ram, size, MADV_DOEXEC)) {
- ksft_print_msg("MADV_DOEXEC: %s\n", strerror(errno));
- return errno == EINVAL ? KSFT_SKIP : KSFT_FAIL;
- }
+ if (madvise(ram, size, MADV_DOEXEC))
+ return unsupported();
pin = (struct pin_longterm_test) {
.addr = (uintptr_t)ram,
.size = size,
@@ -358,7 +397,7 @@ static int shared_mm_exec(bool own_thread)
pid_t pid;
if (!ram)
- return errno == EINVAL ? KSFT_SKIP : KSFT_FAIL;
+ return unsupported();
if (pipe(pipe_fd))
return KSFT_FAIL;
if (own_thread) {
@@ -403,7 +442,7 @@ static int vhost_owner_exec(void)
return KSFT_SKIP;
ram = preserved_ram(size);
if (!ram)
- return errno == EINVAL ? KSFT_SKIP : KSFT_FAIL;
+ return unsupported();
if (ioctl(fd, VHOST_SET_OWNER))
return KSFT_SKIP;
if (ioctl(fd, VHOST_RESET_OWNER))
@@ -473,7 +512,7 @@ static int unprivileged_exec(const char *path, bool preserve)
}
ram = preserved_ram(size);
if (!ram)
- _exit(errno == EINVAL ? KSFT_SKIP : KSFT_FAIL);
+ _exit(unsupported());
err = exec_path(path, ram, size, 1, true);
/* Exec must return EPERM with the old mapping intact. */
if (err != EPERM || check_bytes(ram, size, 0x31))
@@ -520,7 +559,7 @@ static int devzero_exec(void)
return KSFT_FAIL;
memset(ram, 0x31, size);
if (madvise(ram, size, MADV_DOEXEC))
- return errno == EINVAL ? KSFT_SKIP : KSFT_FAIL;
+ return unsupported();
check_exec(ram, size, 1, false);
return KSFT_FAIL;
}
@@ -558,7 +597,7 @@ static int noexec_dontexec(void)
goto out;
}
if (madvise(ram, size, MADV_DOEXEC)) {
- ret = errno == EINVAL ? KSFT_SKIP : KSFT_FAIL;
+ ret = unsupported();
goto out;
}
if (!madvise(ram, size, MADV_DONTEXEC) && exec_refused(ram, size))
@@ -577,7 +616,7 @@ static int doexec_noexec(void)
int ret;
if (!ram)
- return errno == EINVAL ? KSFT_SKIP : KSFT_FAIL;
+ return unsupported();
if (!exec_refused(ram, size) || madvise(ram, size, MADV_DONTEXEC) ||
mprotect(ram, size, PROT_READ | PROT_EXEC))
return KSFT_FAIL;
@@ -631,7 +670,7 @@ static int devmem_refused(void)
int fd;
if (!preserved_ram(size))
- return errno == EINVAL ? KSFT_SKIP : KSFT_FAIL;
+ return unsupported();
/* Without FMODE_WRITE, do_mmap() drops VM_SHARED from the mapping. */
fd = open("/dev/mem", O_RDWR);
if (fd < 0)
@@ -681,8 +720,10 @@ static int vfio_bar_exec(void)
uintptr_t aligned;
int dev;
- if (!bdf || !preserved_ram(page))
+ if (!bdf)
return KSFT_SKIP;
+ if (!preserved_ram(page))
+ return unsupported();
dev = vfio_device_fd(bdf);
if (dev < 0 || ioctl(dev, VFIO_DEVICE_GET_REGION_INFO, &info) ||
!(info.flags & VFIO_REGION_INFO_FLAG_MMAP) || info.size < page) {
@@ -725,7 +766,7 @@ static int nonleader_exec(void)
pthread_t thread;
if (!ram)
- return errno == EINVAL ? KSFT_SKIP : KSFT_FAIL;
+ return unsupported();
if (pthread_create(&thread, NULL, exec_thread, ram))
return KSFT_FAIL;
pthread_join(thread, NULL);
@@ -773,7 +814,7 @@ static int proc_readers_exec(void)
close(ready[0]);
close(go[1]);
if (!ram)
- _exit(errno == EINVAL ? KSFT_SKIP : KSFT_FAIL);
+ _exit(unsupported());
if (write(ready[1], "x", 1) != 1 || read(go[0], buffer, 1) != 1)
_exit(KSFT_FAIL);
close(ready[1]);
@@ -910,7 +951,7 @@ static int clone_exec_race(void)
close(report[0]);
race.ram = preserved_ram(size);
if (!race.ram)
- _exit(errno == EINVAL ? KSFT_SKIP : KSFT_FAIL);
+ _exit(unsupported());
if (pthread_create(&thread, NULL, clone_race_thread, &race))
_exit(KSFT_FAIL);
while (atomic_load(&race.rounds) < 4)
@@ -985,7 +1026,7 @@ static int allocation_failures(void)
for (i = 0; i < size; i += stride)
ram[i] = 0x31;
if (madvise(ram, size, MADV_DOEXEC))
- _exit(errno == EINVAL ? KSFT_SKIP : KSFT_FAIL);
+ _exit(unsupported());
/* Keep the path lookup, which /proc fails with ENOENT, out of the sweep. */
exe = open("/proc/self/exe", O_RDONLY | O_CLOEXEC);
fd = open("/proc/self/fail-nth", O_WRONLY | O_CLOEXEC);
@@ -1034,6 +1075,308 @@ static int allocation_failures(void)
return early && fatal ? KSFT_PASS : KSFT_SKIP;
}
+static void exec_mode(const char *exe, const char *mode, void *addr, size_t size)
+{
+ char addr_arg[32], size_arg[32];
+
+ snprintf(addr_arg, sizeof(addr_arg), "%llu",
+ (unsigned long long)(uintptr_t)addr);
+ snprintf(size_arg, sizeof(size_arg), "%zu", size);
+ execl(exe, "doexec", mode, addr_arg, size_arg, NULL);
+ ksft_print_msg("exec: %s\n", strerror(errno));
+ fflush(stdout);
+}
+
+static bool range_mapped(char *addr, size_t size)
+{
+ return !msync(addr, size, MS_ASYNC);
+}
+
+static int sysctl_flag(void)
+{
+ FILE *file = fopen("/proc/sys/vm/madv_doexec_flag", "r");
+ int flag = -1;
+
+ if (!file) {
+ if (access("/proc/sys/vm", F_OK) || !preserved_ram(sysconf(_SC_PAGESIZE)))
+ return KSFT_SKIP;
+ return fail("MADV_DOEXEC works without the sysctl");
+ }
+ if (fscanf(file, "%d", &flag) != 1)
+ flag = -1;
+ fclose(file);
+ return flag == MADV_DOEXEC ? KSFT_PASS : fail("unexpected madv_doexec_flag");
+}
+
+static bool doexec_refused(char *addr, size_t size, const char *what)
+{
+ if (addr == MAP_FAILED) {
+ ksft_print_msg("%s: mmap: %s\n", what, strerror(errno));
+ /* A security policy may forbid the mapping itself. */
+ return errno == EACCES || errno == EPERM;
+ }
+ if (madvise(addr, size, MADV_DOEXEC) && errno == EINVAL)
+ return true;
+ ksft_print_msg("%s not refused\n", what);
+ return false;
+}
+
+/* Apart from VFIO PFN maps, only private non-stack anonymous memory can be marked. */
+static int refused_mappings(void)
+{
+ size_t size = sysconf(_SC_PAGESIZE);
+ int fd = open("/proc/self/exe", O_RDONLY);
+ char stack_var = 0;
+ bool ok = true;
+
+ if (!preserved_ram(size))
+ return unsupported();
+ ok &= doexec_refused(mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0), size, "shared anon");
+ ok &= doexec_refused(mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0), size,
+ "private file");
+ ok &= doexec_refused(mmap(NULL, size, PROT_READ | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), size, "executable");
+ ok &= doexec_refused(mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0), size,
+ "MAP_GROWSDOWN");
+ ok &= doexec_refused((char *)((uintptr_t)&stack_var & ~(size - 1)), size, "stack");
+ close(fd);
+ return ok ? KSFT_PASS : KSFT_FAIL;
+}
+
+static int hugetlb_refused(void)
+{
+ size_t size = 2ul << 20;
+ char *addr;
+
+ if (!preserved_ram(sysconf(_SC_PAGESIZE)))
+ return unsupported();
+ addr = mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
+ if (addr == MAP_FAILED)
+ return KSFT_SKIP;
+ return doexec_refused(addr, size, "hugetlb") ? KSFT_PASS : KSFT_FAIL;
+}
+
+static int unmarked_exec(void)
+{
+ size_t size = sysconf(_SC_PAGESIZE);
+ char *ram = preserved_ram(size);
+
+ if (!ram)
+ return unsupported();
+ if (madvise(ram, size, MADV_DONTEXEC) || madvise(ram, size, MADV_DONTEXEC))
+ return fail("MADV_DONTEXEC failed");
+ exec_mode("/proc/self/exe", "--absent", ram, size);
+ return KSFT_FAIL;
+}
+
+static int after_absent(char *ram, size_t size)
+{
+ return range_mapped(ram, size) ? fail("range still mapped") : KSFT_PASS;
+}
+
+/* Marking the middle page splits the VMA; only that page carries over. */
+static int partial_exec(void)
+{
+ size_t page = sysconf(_SC_PAGESIZE);
+ char *ram = mmap(NULL, 3 * page, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ if (ram == MAP_FAILED)
+ return KSFT_FAIL;
+ memset(ram, 0x31, 3 * page);
+ if (madvise(ram + page, page, MADV_DOEXEC))
+ return unsupported();
+ exec_mode("/proc/self/exe", "--partial", ram, 3 * page);
+ return KSFT_FAIL;
+}
+
+static int after_partial(char *ram, size_t size)
+{
+ size_t page = size / 3;
+
+ if (range_mapped(ram, page) || range_mapped(ram + 2 * page, page))
+ return fail("unmarked page preserved");
+ if (!range_mapped(ram + page, page))
+ return fail("marked page not preserved");
+ return check_contents(ram + page, page, 0x31);
+}
+
+/* The flag is checked before exec too, so that its absence after is meaningful. */
+static int exec_with_flag(char *ram, size_t size, const char *mode, const char *flag)
+{
+ if (!smaps_has_flag(ram, flag))
+ return fail("flag missing before exec");
+ exec_mode("/proc/self/exe", mode, ram, size);
+ return KSFT_FAIL;
+}
+
+static int flagged_exec(const char *mode, int advice, const char *flag)
+{
+ size_t size = sysconf(_SC_PAGESIZE);
+ char *ram = preserved_ram(size);
+
+ if (!ram)
+ return unsupported();
+ if (madvise(ram, size, advice))
+ return fail("madvise failed");
+ return exec_with_flag(ram, size, mode, flag);
+}
+
+static int locked_exec(void)
+{
+ size_t size = sysconf(_SC_PAGESIZE);
+ char *ram = preserved_ram(size);
+
+ if (!ram)
+ return unsupported();
+ if (mlock(ram, size))
+ return KSFT_SKIP;
+ return exec_with_flag(ram, size, "--unlocked", " lo ");
+}
+
+static int after_unlocked(char *ram, size_t size)
+{
+ if (smaps_has_flag(ram, " lo "))
+ return fail("VM_LOCKED preserved");
+ return check_contents(ram, size, 0x31);
+}
+
+static int dontfork_exec(void)
+{
+ return flagged_exec("--dontfork", MADV_DONTFORK, " dc ");
+}
+
+static int wipeonfork_exec(void)
+{
+ return flagged_exec("--wipeonfork", MADV_WIPEONFORK, " wf ");
+}
+
+/* The fork flags carry over: a child of the new image sees what they say. */
+static int after_fork_flag(char *ram, size_t size, const char *flag, bool wipe)
+{
+ pid_t pid;
+
+ if (!smaps_has_flag(ram, flag))
+ return fail("fork flag lost");
+ if (check_bytes(ram, size, 0x31))
+ return fail("contents lost");
+ pid = fork();
+ if (pid < 0)
+ return KSFT_FAIL;
+ if (!pid) {
+ if (wipe)
+ _exit(range_mapped(ram, size) ? check_bytes(ram, size, 0) : KSFT_FAIL);
+ _exit(range_mapped(ram, size) ? KSFT_FAIL : KSFT_PASS);
+ }
+ return wait_child(pid);
+}
+
+static int after_dontfork(char *ram, size_t size)
+{
+ return after_fork_flag(ram, size, " dc ", false);
+}
+
+static int after_wipeonfork(char *ram, size_t size)
+{
+ return after_fork_flag(ram, size, " wf ", true);
+}
+
+/* The userfaultfd context belongs to the old mm and must not carry over. */
+static int uffd_exec(void)
+{
+ size_t size = sysconf(_SC_PAGESIZE);
+ char *ram = preserved_ram(size);
+ struct uffdio_api api = { .api = UFFD_API };
+ struct uffdio_register reg = {
+ .mode = UFFDIO_REGISTER_MODE_MISSING,
+ };
+ int uffd;
+
+ if (!ram)
+ return unsupported();
+ uffd = syscall(__NR_userfaultfd, O_CLOEXEC | UFFD_USER_MODE_ONLY);
+ if (uffd < 0 || ioctl(uffd, UFFDIO_API, &api))
+ return KSFT_SKIP;
+ reg.range.start = (uintptr_t)ram;
+ reg.range.len = size;
+ if (ioctl(uffd, UFFDIO_REGISTER, ®))
+ return KSFT_SKIP;
+ if (!smaps_has_flag(ram, " um "))
+ return fail("userfaultfd registration not in smaps");
+ exec_mode("/proc/self/exe", "--no-uffd", ram, size);
+ return KSFT_FAIL;
+}
+
+static int after_no_uffd(char *ram, size_t size)
+{
+ if (smaps_has_flag(ram, " um "))
+ return fail("userfaultfd registration preserved");
+ return check_contents(ram, size, 0x31);
+}
+
+/* vma_dup_policy() carries the memory policy of the range over. */
+static int mempolicy_exec(void)
+{
+ size_t size = sysconf(_SC_PAGESIZE);
+ char *ram = preserved_ram(size);
+ unsigned long nodes = 1;
+
+ if (!ram)
+ return unsupported();
+ if (syscall(__NR_mbind, ram, size, MPOL_PREFERRED, &nodes, 2, 0))
+ return KSFT_SKIP;
+ exec_mode("/proc/self/exe", "--mempolicy", ram, size);
+ return KSFT_FAIL;
+}
+
+static int after_mempolicy(char *ram, size_t size)
+{
+ unsigned long nodes[16] = { 0 };
+ int mode = -1;
+
+ if (syscall(__NR_get_mempolicy, &mode, nodes, 8 * sizeof(nodes), ram, MPOL_F_ADDR))
+ return fail("get_mempolicy failed");
+ if (mode != MPOL_PREFERRED || nodes[0] != 1)
+ return fail("memory policy lost");
+ return check_contents(ram, size, 0x31);
+}
+
+struct after_mode {
+ const char *mode;
+ int (*fn)(char *ram, size_t size);
+};
+
+static const struct after_mode after_modes[] = {
+ { "--absent", after_absent },
+ { "--partial", after_partial },
+ { "--unlocked", after_unlocked },
+ { "--dontfork", after_dontfork },
+ { "--wipeonfork", after_wipeonfork },
+ { "--no-uffd", after_no_uffd },
+ { "--mempolicy", after_mempolicy },
+};
+
+struct more_case {
+ const char *name;
+ int (*fn)(void);
+};
+
+static const struct more_case more_cases[] = {
+ { "madv_doexec_flag sysctl matches MADV_DOEXEC", sysctl_flag },
+ { "shared, file, executable and stack mappings refused", refused_mappings },
+ { "hugetlb mapping refused", hugetlb_refused },
+ { "MADV_DONTEXEC: range not preserved", unmarked_exec },
+ { "partially marked VMA: only the marked part preserved", partial_exec },
+ { "mlocked range: preserved unlocked", locked_exec },
+ { "MADV_DONTFORK range: preserved, not given to a child", dontfork_exec },
+ { "MADV_WIPEONFORK range: preserved, wiped in a child", wipeonfork_exec },
+ { "userfaultfd registration: not preserved", uffd_exec },
+ { "memory policy: preserved", mempolicy_exec },
+};
+
int main(int argc, char **argv)
{
pid_t pid;
@@ -1052,6 +1395,10 @@ int main(int argc, char **argv)
strtoull(argv[3], NULL, 10), atoi(argv[4]), atoi(argv[5]));
if (argc == 5 && !strcmp(argv[1], "--reject"))
return KSFT_FAIL;
+ for (kind = 0; argc == 4 && kind < (int)ARRAY_SIZE(after_modes); kind++)
+ if (!strcmp(argv[1], after_modes[kind].mode))
+ return after_modes[kind].fn((char *)(uintptr_t)strtoull(argv[2], NULL, 10),
+ strtoull(argv[3], NULL, 10));
if (argc == 2 && !strcmp(argv[1], "--euid"))
return geteuid() ? KSFT_SKIP : KSFT_PASS;
if (argc == 4 && !strcmp(argv[1], "--pfnmap"))
@@ -1070,18 +1417,20 @@ int main(int argc, char **argv)
write(fd, "0", 1);
close(fd);
}
+ if (!range_mapped(ram, size))
+ return fail("range not preserved");
for (i = 0; i < size; i += stride)
if (ram[i] != 0x31)
return KSFT_FAIL;
return KSFT_PASS;
}
ksft_print_header();
- ksft_set_plan(NR_TESTS + 13);
+ ksft_set_plan(NR_TESTS + 13 + ARRAY_SIZE(more_cases));
for (kind = 0; kind < NR_TESTS; kind++) {
fflush(stdout);
pid = fork();
if (!pid)
- _exit(before_exec(kind));
+ exit(before_exec(kind));
ret = pid < 0 ? KSFT_FAIL : wait_child(pid);
if (ret == KSFT_SKIP)
ksft_test_result_skip("%s: DOEXEC, GUP test or mapping unavailable\n",
@@ -1093,7 +1442,7 @@ int main(int argc, char **argv)
fflush(stdout);
pid = fork();
if (!pid)
- _exit(before_cow_exec());
+ exit(before_cow_exec());
ret = pid < 0 ? KSFT_FAIL : wait_child(pid);
if (ret == KSFT_SKIP)
ksft_test_result_skip("pre-existing COW: DOEXEC unavailable\n");
@@ -1121,27 +1470,27 @@ int main(int argc, char **argv)
switch (kind) {
case 0:
case 1:
- _exit(shared_mm_exec(kind));
+ exit(shared_mm_exec(kind));
case 2:
- _exit(vhost_owner_exec());
+ exit(vhost_owner_exec());
case 3:
- _exit(allocation_failures());
+ exit(allocation_failures());
case 4:
- _exit(nonleader_exec());
+ exit(nonleader_exec());
case 5:
- _exit(proc_readers_exec());
+ exit(proc_readers_exec());
case 6:
- _exit(clone_exec_race());
+ exit(clone_exec_race());
case 7:
- _exit(setuid_exec());
+ exit(setuid_exec());
case 8:
- _exit(devzero_exec());
+ exit(devzero_exec());
case 9:
- _exit(doexec_noexec());
+ exit(doexec_noexec());
case 10:
- _exit(devmem_refused());
+ exit(devmem_refused());
case 11:
- _exit(vfio_bar_exec());
+ exit(vfio_bar_exec());
}
}
ret = pid < 0 ? KSFT_FAIL : wait_child(pid);
@@ -1150,5 +1499,16 @@ int main(int argc, char **argv)
else
ksft_test_result(ret == KSFT_PASS, "%s\n", extra_names[kind]);
}
+ for (kind = 0; kind < (int)ARRAY_SIZE(more_cases); kind++) {
+ fflush(stdout);
+ pid = fork();
+ if (!pid)
+ exit(more_cases[kind].fn());
+ ret = pid < 0 ? KSFT_FAIL : wait_child(pid);
+ if (ret == KSFT_SKIP)
+ ksft_test_result_skip("%s: unavailable\n", more_cases[kind].name);
+ else
+ ksft_test_result(ret == KSFT_PASS, "%s\n", more_cases[kind].name);
+ }
ksft_finished();
}
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
next prev parent reply other threads:[~2026-10-14 17:16 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-14 17:14 [Devel] [PATCH hci-8.1 0/7] selftests/mm: broader MADV_DOEXEC coverage Konstantin Khorenko
2026-10-14 17:14 ` Konstantin Khorenko [this message]
2026-10-14 17:15 ` [Devel] [PATCH hci-8.1 2/7] selftests/mm: cover the MADV_DOEXEC opt-in note and the new stack Konstantin Khorenko
2026-10-14 17:15 ` [Devel] [PATCH hci-8.1 3/7] selftests/mm: cover large, swapped and partly marked THP ranges Konstantin Khorenko
2026-10-14 17:15 ` [Devel] [PATCH hci-8.1 4/7] selftests/mm: check that MADV_DOEXEC refuses an exec from a vfork() child Konstantin Khorenko
2026-10-14 17:15 ` [Devel] [PATCH hci-8.1 5/7] selftests/mm: check VM_MAYEXEC of MADV_DOEXEC ranges in smaps Konstantin Khorenko
2026-10-14 17:15 ` [Devel] [PATCH hci-8.1 6/7] selftests/mm: check who can still reach the old mm of a preserving exec Konstantin Khorenko
2026-10-14 17:15 ` [Devel] [PATCH hci-8.1 7/7] selftests/mm: say why a doexec case fails Konstantin Khorenko
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=20261014-b4-prep-khorenko-vstor-149847-5-doexec-selftests-v1-1-b23b454691db@virtuozzo.com \
--to=khorenko@virtuozzo.com \
--cc=andrey.drobyshev@virtuozzo.com \
--cc=devel@openvz.org \
--cc=ptikhomirov@virtuozzo.com \
--cc=vasileios.almpanis@virtuozzo.com \
/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 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.