@samitouri / QOSamiQemu / commits / 6b5aef7cac

linux-user: Fix AT_EXECFN in AUXV for symlinked programs

The AT_EXECFN entry in AUXV needs to keep the value which was used when the program was started. Especially for symlinked programs qemu should not try to resolve the realpath. Here is a reproducer: (arm64-chroot)root@p100:/# cd /usr/bin (arm64-chroot)root@p100:/usr/bin# ln -s echo testprog (arm64-chroot)root@p100:/usr/bin# LD_SHOW_AUXV=1 ./testprog | grep AT_EXECFN AT_EXECFN: ./testprog In this example, "./testprog" is the correct output, and not "/usr/bin/echo". This patch fixes parts of commit 258bec39 ("linux-user: Fix access to /proc/self/exe"). Fixes: 258bec39 ("linux-user: Fix access to /proc/self/exe") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3379 Signed-off-by: Helge Deller <deller@gmx.de>

Helge Deller committed May 1, 2026 at 12:56 UTC 6b5aef7cac9dab7c16451588cff6615eb2048293
3 files changed +12 -9
linux-user/main.c
+4 -2
@@ -772,8 +772,10 @@ int main(int argc, char **argv, char **envp)
772 }
773
774 /* Resolve executable file name to full path name */
775 - if (realpath(exec_path, real_exec_path)) {
776 - exec_path = real_exec_path;
775 + /* Keep how we started the program in exec_path, e.g. "./my_program" */
776 + /* Store real path in real_exec_path, e.g. "/usr/local/bin/my_program" */
777 + if (!realpath(exec_path, real_exec_path)) {
778 + printf("Could not resolve %s\n", exec_path);
779 }
780
781 /*
linux-user/syscall.c
+7 -7
@@ -8790,9 +8790,9 @@ static int maybe_do_fake_open(CPUArchState *cpu_env, int dirfd,
8790 return -1;
8791 }
8792 if (safe) {
8793 - return safe_openat(dirfd, exec_path, flags, mode);
8793 + return safe_openat(dirfd, real_exec_path, flags, mode);
8794 } else {
8795 - return openat(dirfd, exec_path, flags, mode);
8795 + return openat(dirfd, real_exec_path, flags, mode);
8796 }
8797 }
8798
@@ -8929,9 +8929,9 @@ ssize_t do_guest_readlink(const char *pathname, char *buf, size_t bufsiz)
8929 * Don't worry about sign mismatch as earlier mapping
8930 * logic would have thrown a bad address error.
8931 */
8932 - ret = MIN(strlen(exec_path), bufsiz);
8932 + ret = MIN(strlen(real_exec_path), bufsiz);
8933 /* We cannot NUL terminate the string. */
8934 - memcpy(buf, exec_path, ret);
8934 + memcpy(buf, real_exec_path, ret);
8935 } else {
8936 ret = readlink(path(pathname), buf, bufsiz);
8937 }
@@ -9022,7 +9022,7 @@ static int do_execv(CPUArchState *cpu_env, int dirfd,
9022
9023 const char *exe = p;
9024 if (is_proc_myself(p, "exe")) {
9025 - exe = exec_path;
9025 + exe = real_exec_path;
9026 }
9027 ret = is_execveat
9028 ? safe_execveat(dirfd, exe, argp, envp, flags)
@@ -11033,9 +11033,9 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
11033 * Don't worry about sign mismatch as earlier mapping
11034 * logic would have thrown a bad address error.
11035 */
11036 - ret = MIN(strlen(exec_path), arg4);
11036 + ret = MIN(strlen(real_exec_path), arg4);
11037 /* We cannot NUL terminate the string. */
11038 - memcpy(p2, exec_path, ret);
11038 + memcpy(p2, real_exec_path, ret);
11039 } else {
11040 ret = get_errno(readlinkat(arg1, path(p), p2, arg4));
11041 }
linux-user/user-internals.h
+1
@@ -24,6 +24,7 @@
24 #include "exec/translation-block.h"
25
26 extern char *exec_path;
27 +extern char real_exec_path[PATH_MAX];
28 void init_task_state(TaskState *ts);
29 void task_settid(TaskState *);
30 void stop_all_tasks(void);