A process keeps its pid, its descriptors, its working directory and its
mounts across an exec; what it loses is the address space and the program
in it. From there the work is the same as starting fresh, so this is the
same function -- run_program, told not to build a new kernel.
Close-on-exec descriptors go and the rest survive with their offsets,
which is how a shell hands a pipe to the command it starts. Everything
that can fail is checked before the point of no return: a failed execve
has to *return*, with the guest seeing ENOENT and carrying on in the
program it already had, and once the host starts building a new address
space there is nothing to return to. The strings are read in the kernel
for the same reason -- the memory holding them is about to go away.
The file is read through the guest's namespace, so a sysroot is honoured
and /bin/sh means the guest's.
Two bugs found by the test. F_GETFD answered for a closed descriptor: the
flag vector keeps a slot after the entry is gone, and only the flags were
being checked, so a program could not tell whether execve had closed what
it asked to be closed. That one predates exec. And the failed-exec path
killed the child instead of returning, which is what moved the checks
before the commit point.
QEMU cannot be the oracle here, which is worth stating plainly:
qemu-aarch64 re-execs only itself, for the program it was given, so a
guest that execs another aarch64 binary fails under QEMU and works under
gex. The test therefore checks a written-down expectation of Linux's
behaviour, and the harness grew an "expect" beside "compare" to say so. I
confirmed it fails when the expectation is wrong.
Suite is 44.