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 5/7] selftests/mm: check VM_MAYEXEC of MADV_DOEXEC ranges in smaps
Date: Wed, 14 Oct 2026 19:15:03 +0200 [thread overview]
Message-ID: <20261014-b4-prep-khorenko-vstor-149847-5-doexec-selftests-v1-5-b23b454691db@virtuozzo.com> (raw)
In-Reply-To: <20261014-b4-prep-khorenko-vstor-149847-5-doexec-selftests-v1-0-b23b454691db@virtuozzo.com>
An EACCES from mprotect(PROT_EXEC) does not prove that VM_MAYEXEC is
clear: an LSM or MDWE refuses it the same way. Read the flag from
VmFlags as well. Also check that a fork child and the new image get
VM_MAYEXEC back, as the mark is dropped from their copies.
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 | 94 +++++++++++++++++++++++++------------
1 file changed, 64 insertions(+), 30 deletions(-)
diff --git a/tools/testing/selftests/mm/doexec.c b/tools/testing/selftests/mm/doexec.c
index 01e786a4cb75c..89862e0b6a4dc 100644
--- a/tools/testing/selftests/mm/doexec.c
+++ b/tools/testing/selftests/mm/doexec.c
@@ -583,9 +583,43 @@ static int devzero_exec(void)
return KSFT_FAIL;
}
+/* Copy the line of the smaps entry of the VMA at @addr that starts with @prefix. */
+static bool smaps_line(char *addr, const char *prefix, char *line, size_t len)
+{
+ FILE *file = fopen("/proc/self/smaps", "r");
+ bool in_vma = false, found = false;
+ char want[32];
+
+ if (!file)
+ return false;
+ snprintf(want, sizeof(want), "%lx-", (unsigned long)addr);
+ while (!found && fgets(line, len, file)) {
+ char *space = strchr(line, ' ');
+
+ if (space && memchr(line, '-', space - line) && !memchr(line, ':', space - line))
+ in_vma = !strncmp(line, want, strlen(want));
+ else if (in_vma)
+ found = !strncmp(line, prefix, strlen(prefix));
+ }
+ fclose(file);
+ return found;
+}
+
+static bool smaps_has_flag(char *addr, const char *flag)
+{
+ char line[512];
+
+ return smaps_line(addr, "VmFlags:", line, sizeof(line)) && strstr(line, flag);
+}
+
+/* VM_MAYEXEC ("me") is clear, so mprotect(PROT_EXEC) fails. */
static bool exec_refused(char *ram, size_t size)
{
- return mprotect(ram, size, PROT_READ | PROT_EXEC) && errno == EACCES;
+ char line[512];
+
+ return smaps_line(ram, "VmFlags:", line, sizeof(line)) &&
+ !strstr(line, " me ") &&
+ mprotect(ram, size, PROT_READ | PROT_EXEC) && errno == EACCES;
}
/* MADV_DONTEXEC must not grant more than mmap() did on a noexec mount. */
@@ -645,35 +679,6 @@ static int doexec_noexec(void)
return ret;
}
-/* Copy the line of the smaps entry of the VMA at @addr that starts with @prefix. */
-static bool smaps_line(char *addr, const char *prefix, char *line, size_t len)
-{
- FILE *file = fopen("/proc/self/smaps", "r");
- bool in_vma = false, found = false;
- char want[32];
-
- if (!file)
- return false;
- snprintf(want, sizeof(want), "%lx-", (unsigned long)addr);
- while (!found && fgets(line, len, file)) {
- char *space = strchr(line, ' ');
-
- if (space && memchr(line, '-', space - line) && !memchr(line, ':', space - line))
- in_vma = !strncmp(line, want, strlen(want));
- else if (in_vma)
- found = !strncmp(line, prefix, strlen(prefix));
- }
- fclose(file);
- return found;
-}
-
-static bool smaps_has_flag(char *addr, const char *flag)
-{
- char line[512];
-
- return smaps_line(addr, "VmFlags:", line, sizeof(line)) && strstr(line, flag);
-}
-
static int after_pfnmap_exec(char *bar, size_t size)
{
size_t page = sysconf(_SC_PAGESIZE), i;
@@ -1274,6 +1279,33 @@ static int locked_exec(void)
return exec_with_flag(ram, size, "--unlocked", " lo ");
}
+/* Dropping the mark in a copy gives VM_MAYEXEC back. */
+static int mayexec_exec(void)
+{
+ size_t size = sysconf(_SC_PAGESIZE);
+ char *ram = preserved_ram(size);
+ pid_t pid;
+
+ if (!ram)
+ return unsupported();
+ if (!exec_refused(ram, size))
+ return fail("VM_MAYEXEC kept by MADV_DOEXEC");
+ pid = fork();
+ if (!pid)
+ _exit(smaps_has_flag(ram, " me ") ? KSFT_PASS : KSFT_FAIL);
+ if (pid < 0 || wait_child(pid) != KSFT_PASS)
+ return fail("VM_MAYEXEC not back in a fork child");
+ exec_mode("/proc/self/exe", "--mayexec", ram, size);
+ return KSFT_FAIL;
+}
+
+static int after_mayexec(char *ram, size_t size)
+{
+ if (!smaps_has_flag(ram, " me "))
+ return fail("VM_MAYEXEC not back after exec");
+ return check_contents(ram, size, 0x31);
+}
+
static int after_unlocked(char *ram, size_t size)
{
if (smaps_has_flag(ram, " lo "))
@@ -1832,6 +1864,7 @@ static const struct after_mode after_modes[] = {
{ "--no-uffd", after_no_uffd },
{ "--mempolicy", after_mempolicy },
{ "--thp-half", after_thp_half },
+ { "--mayexec", after_mayexec },
};
struct more_case {
@@ -1886,6 +1919,7 @@ static const struct more_case more_cases[] = {
{ "swapped out pages: preserved", swapped_exec },
{ "half of a THP: preserved", thp_half_exec },
{ "vfork() child: preserving exec refused", vfork_exec },
+ { "exec permission back in a fork child and the new image", mayexec_exec },
};
int main(int argc, char **argv)
--
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 ` [Devel] [PATCH hci-8.1 1/7] selftests/mm: cover the MADV_DOEXEC interface and VMA attributes Konstantin Khorenko
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 ` Konstantin Khorenko [this message]
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-5-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.