All Virtuozzo development lists (kernel + QEMU)
 help / color / mirror / Atom feed
From: no-reply@sashiko.virtuozzo.com
To: Konstantin Khorenko <khorenko@virtuozzo.com>,
	Pavel Tikhomirov <ptikhomirov@virtuozzo.com>,
	Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>,
	Vasileios Almpanis <vasileios.almpanis@virtuozzo.com>
Cc: devel@openvz.org
Subject: Re: [Devel] [PATCH hci-8.1 8/8] selftests/mm: test memory preservation across exec
Date: Wed, 14 Oct 2026 17:33:38 +0000	[thread overview]
Message-ID: <010701a13b7a1414-cc670e39-401a-4e02-9898-a6f0dd19a8ee-000000@eu-central-1.amazonses.com> (raw)
In-Reply-To: <20261014-b4-prep-khorenko-vstor-149847-2-doexec-pinned-memory-v1-8-36f1120f9b57@virtuozzo.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Low] selftests/mm: doexec child diagnostics are lost by _exit()/exec
- [Low] selftests/mm: doexec ignores write() return values
- [Low] selftests/mm: doexec treats any SIGSEGV in allocation sweep as expected
--

commit eee32e73516b760bb3007e1b97633a4999581fec
Author: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

selftests/mm: test memory preservation across exec

This commit adds doexec, a selftest for MADV_DOEXEC. It checks that memory
backing long-term GUP pins and other mappings survives repeated exec and
fork. It also covers CLONE_VM handling, threads, /proc readers and an
optional allocation-failure sweep. The test is wired into run_vmtests.sh.

Link: https://virtuozzo.atlassian.net/browse/VSTOR-150231

> diff --git a/tools/testing/selftests/mm/doexec.c b/tools/testing/selftests/mm/doexec.c
> new file mode 100644
> --- /dev/null
> +++ b/tools/testing/selftests/mm/doexec.c

[ ... ]

> +static void exec_stage(int fd, char *ram, size_t size, int kind, int stage)
> +{
> +	char fd_arg[32], addr_arg[32], size_arg[32], kind_arg[32], stage_arg[32];
> +
> [ ... ]
> +	execl("/proc/self/exe", "doexec", "--exec", fd_arg, addr_arg, size_arg,
> +	      kind_arg, stage_arg, NULL);
> +	ksft_print_msg("exec: %s\n", strerror(errno));
> +	_exit(KSFT_FAIL);
> +}

[Severity: Low]
Can the diagnostics printed by the forked children get lost here?

ksft_print_msg() writes through buffered stdio. When stdout is a pipe or a
file, as under run_vmtests.sh or a CI harness, the "exec: ..." message is
still in the buffer when _exit(KSFT_FAIL) runs, and _exit() does not flush
it.

The same applies to the other messages printed from the per-test children,
for example:

    before_exec()  -> "pin: ...", "MADV_DOEXEC: ..."
    check_pin()    -> "read pin: ...", "CPU/pin mismatch at byte N"
    allocation_failures() -> "fail-nth N: ..."

In main(), the fflush(stdout) calls run in the parent before fork(). They
prevent duplicated output but do not flush what the child prints itself.
Messages printed before the execl() into the --exec stage are likewise
discarded by the exec.

A failing run would then show only a bare "not ok" line. Would an
fflush(stdout) before each _exit() and execl() in the children be
appropriate?

> +static int shared_mm_exec(bool own_thread)
> +{

[ ... ]

> +	if (own_thread) {
> +		if (pthread_create(&thread, NULL, blocked_thread, &pipe_fd[0]))
> +			return KSFT_FAIL;
> +		check_exec(ram, size, 1, false);
> +		write(pipe_fd[1], "x", 1);
> +		pthread_join(thread, NULL);
> +		return KSFT_FAIL;
> +	}

[ ... ]

> +	write(pipe_fd[1], "x", 1);
> +	if (wait_child(pid))
> +		ret = KSFT_FAIL;

[Severity: Low]
Is it intended that the write() results are ignored here?

write() is warn_unused_result under glibc _FORTIFY_SOURCE, so this can
produce -Wunused-result warnings.

If this write ever failed, the CLONE_VM child or thread would stay blocked
in read(), and wait_child() or pthread_join() would hang because the test
has no timeout.

> +static int clone_race_child(void *arg)
> +{
> +	struct clone_race *race = arg;
> +	unsigned int i;
> +
> +	for (i = 0; i < 8; i++) {
> +		if (__atomic_load_n(race->ram, __ATOMIC_RELAXED) != 0x31) {
> +			write(race->report, "x", 1);
> +			return KSFT_FAIL;
> +		}
> +		usleep(1000);
> +	}
> +	return KSFT_PASS;
> +}

[Severity: Low]
Should the result of this write() be checked as well?

This write is the only signal that the shared memory was stolen. If it fails,
clone_exec_race() sees EOF on the report pipe and passes despite the detected
failure. clone_race_thread() and the --check handler in main() also drop
write() results.

> +	if (argc == 5 && !strcmp(argv[1], "--check")) {
> +		char *ram = (char *)(uintptr_t)strtoull(argv[2], NULL, 10);
> +		size_t size = strtoull(argv[3], NULL, 10);
> +		size_t stride = strtoull(argv[4], NULL, 10), i;
> +		int fd = open("/proc/self/fail-nth", O_WRONLY);
> +
> +		if (fd >= 0) {
> +			write(fd, "0", 1);
> +			close(fd);
> +		}
> +		for (i = 0; i < size; i += stride)
> +			if (ram[i] != 0x31)
> +				return KSFT_FAIL;
> +		return KSFT_PASS;
> +	}

[ ... ]

> +		if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV)
> +			fatal++;

[ ... ]

> +	/* A sweep that injected no recoverable/fatal errors provides no coverage. */
> +	return early && fatal ? KSFT_PASS : KSFT_SKIP;
> +}

[Severity: Low]
Can the allocation_failures() sweep pass even when the preserved mapping has
been lost?

do_execveat_common() deliberately raises SIGSEGV when exec fails after the
point of no return:

    if (bprm->point_of_no_return && !fatal_signal_pending(current))
            force_fatal_sig(SIGSEGV);

So SIGSEGV is a legitimate outcome of the sweep. However, the test cannot
tell that kernel-forced SIGSEGV apart from a user-space fault.

After a successful preserving exec, the --check image dereferences ram[i]
in main(). If a kernel bug dropped or unmapped the preserved range, that load
would also raise SIGSEGV. allocation_failures() would count it as fatal++.

The final check is "early && fatal ? KSFT_PASS : KSFT_SKIP", and it requires
at least one SIGSEGV. A regression that loses the mapping can therefore help
the test pass.

Is there a way to distinguish the two cases? One option is to have the --check
image report a lost mapping without faulting, for example by probing the range
with mincore() or msync() before reading it.

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261014-b4-prep-khorenko-vstor-149847-2-doexec-pinned-memory-v1-0-36f1120f9b57@virtuozzo.com?part=8

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

      reply	other threads:[~2026-10-14 17:35 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-14 17:11 [Devel] [PATCH hci-8.1 0/8] mm: keep DMA-pinned MADV_DOEXEC memory working " Konstantin Khorenko
2026-10-14 17:11 ` [Devel] [PATCH hci-8.1 1/8] oracle/mm: fix VM_BUG_ON hit when preserving memory Konstantin Khorenko
2026-10-14 17:16   ` no-reply
2026-10-14 17:11 ` [Devel] [PATCH hci-8.1 2/8] oracle/mm: hold the source mmap write lock when copying PTEs Konstantin Khorenko
2026-10-14 17:15   ` no-reply
2026-10-14 17:11 ` [Devel] [PATCH hci-8.1 3/8] oracle/mm: do not write protect COW mappings when preserving across exec Konstantin Khorenko
2026-10-14 17:20   ` no-reply
2026-10-14 17:11 ` [Devel] [PATCH hci-8.1 4/8] oracle/mm: write protect preserved PTEs but keep them exclusive Konstantin Khorenko
2026-10-14 17:18   ` no-reply
2026-10-14 17:11 ` [Devel] [PATCH hci-8.1 5/8] oracle/mm: set MMF_HAS_PINNED on the mm receiving preserved pages Konstantin Khorenko
2026-10-14 17:19   ` no-reply
2026-10-14 17:11 ` [Devel] [PATCH hci-8.1 6/8] oracle/mm: zap the preserved ranges of the old mm after copying them Konstantin Khorenko
2026-10-14 17:24   ` no-reply
2026-10-14 17:11 ` [Devel] [PATCH hci-8.1 7/8] exec: protect preserved memory against surviving shared-mm tasks Konstantin Khorenko
2026-10-14 17:24   ` no-reply
2026-10-14 17:11 ` [Devel] [PATCH hci-8.1 8/8] selftests/mm: test memory preservation across exec Konstantin Khorenko
2026-10-14 17:33   ` no-reply [this message]

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=010701a13b7a1414-cc670e39-401a-4e02-9898-a6f0dd19a8ee-000000@eu-central-1.amazonses.com \
    --to=no-reply@sashiko.virtuozzo.com \
    --cc=andrey.drobyshev@virtuozzo.com \
    --cc=devel@openvz.org \
    --cc=khorenko@virtuozzo.com \
    --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.