[Devel] [PATCH hci-8.1 v2 9/9] selftests/mm: do not lose doexec failures
Konstantin Khorenko
khorenko at virtuozzo.com
Wed Oct 21 19:57:22 MSK 2026
Some doexec failures can go unreported:
- write() results are ignored. The clone race reports a failure by
writing to a pipe, so a failed write turns it into a pass, and a
failed wake-up write leaves a shared-mm child blocked forever. glibc
warns about these with _FORTIFY_SOURCE.
- The allocation failure sweep prints the failing exec and calls
_exit() without flushing stdout, so under a pipe the message is lost.
- The writes that disarm fail-nth are not checked, so injection could
still hit the checks that follow a failed disarm.
Check every write(), abort when a race failure cannot be reported, and
flush stdout before that _exit().
https://virtuozzo.atlassian.net/browse/VSTOR-150234
Feature: oracle/mm: MADV_DOEXEC madvise() flag
Assisted-by: LLM
Signed-off-by: Konstantin Khorenko <khorenko at virtuozzo.com>
---
tools/testing/selftests/mm/doexec.c | 30 ++++++++++++++++++++++--------
1 file changed, 22 insertions(+), 8 deletions(-)
diff --git a/tools/testing/selftests/mm/doexec.c b/tools/testing/selftests/mm/doexec.c
index 7e73a98e4ec2a..561e07b4444f0 100644
--- a/tools/testing/selftests/mm/doexec.c
+++ b/tools/testing/selftests/mm/doexec.c
@@ -441,7 +441,8 @@ static int shared_mm_exec(bool own_thread)
if (pthread_create(&thread, NULL, blocked_thread, &pipe_fd[0]))
return fail("pthread_create failed");
check_exec(ram, size, 1, false);
- write(pipe_fd[1], "x", 1);
+ if (write(pipe_fd[1], "x", 1) != 1)
+ return fail_errno("write");
pthread_join(thread, NULL);
return KSFT_FAIL;
}
@@ -460,7 +461,10 @@ static int shared_mm_exec(bool own_thread)
} else {
ret = check_bytes(ram, size, 0x31) ? fail("contents lost") : KSFT_PASS;
}
- write(pipe_fd[1], "x", 1);
+ if (write(pipe_fd[1], "x", 1) != 1) {
+ ret = fail_errno("write");
+ kill(pid, SIGKILL);
+ }
if (wait_child(pid))
ret = KSFT_FAIL;
if (ret)
@@ -966,6 +970,13 @@ struct clone_race {
atomic_uint rounds;
};
+/* A failure that cannot reach the supervisor must still fail the test. */
+static void report_failure(struct clone_race *race)
+{
+ if (write(race->report, "x", 1) != 1)
+ abort();
+}
+
static int clone_race_child(void *arg)
{
struct clone_race *race = arg;
@@ -973,7 +984,7 @@ static int clone_race_child(void *arg)
for (i = 0; i < 8; i++) {
if (__atomic_load_n(race->ram, __ATOMIC_RELAXED) != 0x31) {
- write(race->report, "x", 1);
+ report_failure(race);
return KSFT_FAIL;
}
usleep(1000);
@@ -989,7 +1000,7 @@ static void *clone_race_thread(void *arg)
pid_t pid;
if (stack == MAP_FAILED) {
- write(race->report, "x", 1);
+ report_failure(race);
atomic_store(&race->rounds, 4);
return NULL;
}
@@ -998,9 +1009,9 @@ static void *clone_race_thread(void *arg)
CLONE_VM | SIGCHLD, race);
if (pid >= 0) {
if (wait_child(pid))
- write(race->report, "x", 1);
+ report_failure(race);
} else if (errno != EBUSY && errno != EINTR) {
- write(race->report, "x", 1);
+ report_failure(race);
}
atomic_fetch_add(&race->rounds, 1);
usleep(1000);
@@ -1122,7 +1133,8 @@ static int allocation_failures(void)
_exit(fail_errno("fail-nth setup"));
err = check_exec_fd(exe, ram, size, stride);
/* Disarm before checking mappings or exiting. */
- pwrite(fd, "0", 1, 0);
+ if (pwrite(fd, "0", 1, 0) != 1)
+ _exit(fail_errno("fail-nth disarm"));
for (i = 0; i < size; i += stride)
if (ram[i] != 0x31)
_exit(fail("contents lost by a failed exec"));
@@ -1135,6 +1147,7 @@ static int allocation_failures(void)
err == E2BIG || err == ELIBBAD || err == EIO)
_exit(10);
ksft_print_msg("fail-nth %u: exec: %s\n", nth, strerror(err));
+ fflush(stdout);
_exit(KSFT_FAIL);
}
do {
@@ -2395,7 +2408,8 @@ int main(int argc, char **argv)
int fd = open("/proc/self/fail-nth", O_WRONLY);
if (fd >= 0) {
- write(fd, "0", 1);
+ if (write(fd, "0", 1) != 1)
+ return fail_errno("fail-nth disarm");
close(fd);
}
if (!range_mapped(ram, size))
--
2.43.0
More information about the Devel
mailing list