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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox