Skip to content

linux-user: openat on /proc/self/exe can return a closed file descriptor

Host environment

  • Operating system: Docker in macOS
  • OS/kernel version: macOS 12.3, Linux 5.10.104-linuxkit in Docker
  • Architecture: arm64
  • QEMU flavor: qemu-user (in my case, qemu-x86_64)
  • QEMU version: qemu-x86_64 version 6.2.0 (v6.2.0)

Description of problem

open("/proc/self/exe", ...) returns a closed file descriptor if qemu-user was executed as an interpreter, passing a file descriptor in the AT_EXECFD auxval.

When the AT_EXECFD auxval is nonzero the user program is loaded through load_elf_binary() (in linux-user/elfload.c) which ultimately calls load_elf_image() with that same file descriptor, and load_elf_image() closes the file descriptor before returning.

do_openat in linux-user/syscall.c will return that file descriptor to the user if the opened path satisfies is_proc_myself(pathname, "exe"), which is obviously wrong both in that the file descriptor is closed as part of the initialization process of qemu itself, and that the user program would then close that file descriptor and thus the next invocation of open would have the same problem.

Steps to reproduce

This program prints 3 3 in a x86_64 docker container on my machine (arm64 macos, which docker desktop handles by running containers in a native linux VM under qemu-user).

#include <fcntl.h>
#include <stdio.h>

int main(int argc, char **argv) {
    int selfexe = open("/proc/self/exe", O_RDONLY | O_CLOEXEC);
    if (selfexe < 0) {
        perror("open self");
        return 1;
    }

    int devnull = open("/dev/null", O_WRONLY | O_CLOEXEC);
    if (devnull < 0) {
        perror("open devnull");
        return 1;
    }

    printf("%d %d\n", selfexe, devnull);
}

Additional information

Thanks to @pm215 for helping me pinpoint the exact issue I was encountering.

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information