All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/4] linux user: make execfd global (like exec path) and keep it open
@ 2021-02-25 20:54 aladjev.andrew
  2021-02-25 20:54 ` [PATCH 2/4] linux user: moved is proc functions to separate file aladjev.andrew
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: aladjev.andrew @ 2021-02-25 20:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: Andrew Aladjev

From: Andrew Aladjev <aladjev.andrew@gmail.com>

User opens /proc/self/exe symlink, than kernel should create /proc/self/fd/<execfd> symlink. We should be able to detect both exe and fd/<execfd> symlinks to provide common behaviour. The easiest solution is to make execfd global and keep it open. This solution looks acceptable because exec_path is already global. PS load_flt_binary is not closing bprm->fd, so load_elf_binary may not close it too (used symmetrically in loader_exec).
---
 linux-user/elfload.c |  3 ++-
 linux-user/exit.c    |  2 ++
 linux-user/main.c    |  2 +-
 linux-user/qemu.h    |  1 +
 linux-user/syscall.c | 16 ++++++++++++----
 5 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index bab4237..4c347b0 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -2600,6 +2600,7 @@ static bool parse_elf_properties(int image_fd,
 
    IMAGE_NAME is the filename of the image, to use in error messages.
    IMAGE_FD is the open file descriptor for the image.
+   WARNING: IMAGE_FD won't be closed.
 
    BPRM_BUF is a copy of the beginning of the file; this of course
    contains the elf file header at offset 0.  It is assumed that this
@@ -2910,7 +2911,6 @@ static void load_elf_image(const char *image_name, int image_fd,
 
     mmap_unlock();
 
-    close(image_fd);
     return;
 
  exit_read:
@@ -2953,6 +2953,7 @@ static void load_elf_interp(const char *filename, struct image_info *info,
     }
 
     load_elf_image(filename, fd, info, NULL, bprm_buf);
+    close(fd);
 }
 
 static int symfind(const void *s0, const void *s1)
diff --git a/linux-user/exit.c b/linux-user/exit.c
index 70b3440..cc9cf38 100644
--- a/linux-user/exit.c
+++ b/linux-user/exit.c
@@ -28,6 +28,8 @@ extern void __gcov_dump(void);
 
 void preexit_cleanup(CPUArchState *env, int code)
 {
+    close(execfd);
+
 #ifdef CONFIG_GPROF
         _mcleanup();
 #endif
diff --git a/linux-user/main.c b/linux-user/main.c
index 81f48ff..d365335 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -50,6 +50,7 @@
 #include "crypto/init.h"
 
 char *exec_path;
+int execfd;
 
 int singlestep;
 static const char *argv0;
@@ -628,7 +629,6 @@ int main(int argc, char **argv, char **envp)
     int target_argc;
     int i;
     int ret;
-    int execfd;
     int log_mask;
     unsigned long max_reserved_va;
 
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 52c9817..ec26730 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -160,6 +160,7 @@ typedef struct TaskState {
 } __attribute__((aligned(16))) TaskState;
 
 extern char *exec_path;
+extern int execfd;
 void init_task_state(TaskState *ts);
 void task_settid(TaskState *);
 void stop_all_tasks(void);
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 389ec09..c171dea 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -8110,8 +8110,7 @@ static int do_openat(void *cpu_env, int dirfd, const char *pathname, int flags,
     };
 
     if (is_proc_myself(pathname, "exe")) {
-        int execfd = qemu_getauxval(AT_EXECFD);
-        return execfd ? execfd : safe_openat(dirfd, exec_path, flags, mode);
+        return execfd;
     }
 
     for (fake_open = fakes; fake_open->filename; fake_open++) {
@@ -8369,8 +8368,17 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
         return ret;
 #endif
     case TARGET_NR_close:
-        fd_trans_unregister(arg1);
-        return get_errno(close(arg1));
+        {
+            int fd = arg1;
+
+            /* We don't need to close execfd, it will be closed on QEMU exit. */
+            if (fd == execfd) {
+                return 0;
+            }
+
+            fd_trans_unregister(fd);
+            return get_errno(close(fd));
+        }
 
     case TARGET_NR_brk:
         return do_brk(arg1);
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/4] linux user: moved is proc functions to separate file
  2021-02-25 20:54 [PATCH 1/4] linux user: make execfd global (like exec path) and keep it open aladjev.andrew
@ 2021-02-25 20:54 ` aladjev.andrew
  2021-02-25 20:54 ` [PATCH 3/4] linux user: refactored is proc myself, added support for fd/<execfd> aladjev.andrew
  2021-02-25 20:54 ` [PATCH 4/4] linux user: added tests for proc myself aladjev.andrew
  2 siblings, 0 replies; 4+ messages in thread
From: aladjev.andrew @ 2021-02-25 20:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: Andrew Aladjev

From: Andrew Aladjev <aladjev.andrew@gmail.com>

---
 linux-user/meson.build    |  1 +
 linux-user/syscall.c      | 33 +--------------------------------
 linux-user/syscall_proc.c | 32 ++++++++++++++++++++++++++++++++
 linux-user/syscall_proc.h |  7 +++++++
 4 files changed, 41 insertions(+), 32 deletions(-)
 create mode 100644 linux-user/syscall_proc.c
 create mode 100644 linux-user/syscall_proc.h

diff --git a/linux-user/meson.build b/linux-user/meson.build
index 7fe28d6..b6a204d 100644
--- a/linux-user/meson.build
+++ b/linux-user/meson.build
@@ -9,6 +9,7 @@ linux_user_ss.add(files(
   'signal.c',
   'strace.c',
   'syscall.c',
+  'syscall_proc.c',
   'uaccess.c',
   'uname.c',
 ))
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index c171dea..0ead34b 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -133,6 +133,7 @@
 #include "qapi/error.h"
 #include "fd-trans.h"
 #include "tcg/tcg.h"
+#include "syscall_proc.h"
 
 #ifndef CLONE_IO
 #define CLONE_IO                0x80000000      /* Clone io context */
@@ -7979,38 +7980,6 @@ static int open_self_auxv(void *cpu_env, int fd)
     return 0;
 }
 
-static int is_proc_myself(const char *filename, const char *entry)
-{
-    if (!strncmp(filename, "/proc/", strlen("/proc/"))) {
-        filename += strlen("/proc/");
-        if (!strncmp(filename, "self/", strlen("self/"))) {
-            filename += strlen("self/");
-        } else if (*filename >= '1' && *filename <= '9') {
-            char myself[80];
-            snprintf(myself, sizeof(myself), "%d/", getpid());
-            if (!strncmp(filename, myself, strlen(myself))) {
-                filename += strlen(myself);
-            } else {
-                return 0;
-            }
-        } else {
-            return 0;
-        }
-        if (!strcmp(filename, entry)) {
-            return 1;
-        }
-    }
-    return 0;
-}
-
-#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) || \
-    defined(TARGET_SPARC) || defined(TARGET_M68K) || defined(TARGET_HPPA)
-static int is_proc(const char *filename, const char *entry)
-{
-    return strcmp(filename, entry) == 0;
-}
-#endif
-
 #if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
 static int open_net_route(void *cpu_env, int fd)
 {
diff --git a/linux-user/syscall_proc.c b/linux-user/syscall_proc.c
new file mode 100644
index 0000000..34051a8
--- /dev/null
+++ b/linux-user/syscall_proc.c
@@ -0,0 +1,32 @@
+#include "qemu/osdep.h"
+
+#include "syscall_proc.h"
+
+int is_proc_myself(const char *filename, const char *entry)
+{
+    if (!strncmp(filename, "/proc/", strlen("/proc/"))) {
+        filename += strlen("/proc/");
+        if (!strncmp(filename, "self/", strlen("self/"))) {
+            filename += strlen("self/");
+        } else if (*filename >= '1' && *filename <= '9') {
+            char myself[80];
+            snprintf(myself, sizeof(myself), "%d/", getpid());
+            if (!strncmp(filename, myself, strlen(myself))) {
+                filename += strlen(myself);
+            } else {
+                return 0;
+            }
+        } else {
+            return 0;
+        }
+        if (!strcmp(filename, entry)) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+int is_proc(const char *filename, const char *entry)
+{
+    return strcmp(filename, entry) == 0;
+}
diff --git a/linux-user/syscall_proc.h b/linux-user/syscall_proc.h
new file mode 100644
index 0000000..3098af9
--- /dev/null
+++ b/linux-user/syscall_proc.h
@@ -0,0 +1,7 @@
+#ifndef SYSCALL_PROC_H
+#define SYSCALL_PROC_H
+
+int is_proc(const char *filename, const char *entry);
+int is_proc_myself(const char *filename, const char *entry);
+
+#endif
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 3/4] linux user: refactored is proc myself, added support for fd/<execfd>
  2021-02-25 20:54 [PATCH 1/4] linux user: make execfd global (like exec path) and keep it open aladjev.andrew
  2021-02-25 20:54 ` [PATCH 2/4] linux user: moved is proc functions to separate file aladjev.andrew
@ 2021-02-25 20:54 ` aladjev.andrew
  2021-02-25 20:54 ` [PATCH 4/4] linux user: added tests for proc myself aladjev.andrew
  2 siblings, 0 replies; 4+ messages in thread
From: aladjev.andrew @ 2021-02-25 20:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: Andrew Aladjev

From: Andrew Aladjev <aladjev.andrew@gmail.com>

---
 linux-user/syscall.c      |  12 ++--
 linux-user/syscall_proc.c | 113 +++++++++++++++++++++++++++++++-------
 linux-user/syscall_proc.h |   5 +-
 3 files changed, 101 insertions(+), 29 deletions(-)

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 0ead34b..00ae823 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -8067,18 +8067,18 @@ static int do_openat(void *cpu_env, int dirfd, const char *pathname, int flags,
         { "auxv", open_self_auxv, is_proc_myself },
         { "cmdline", open_self_cmdline, is_proc_myself },
 #if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
-        { "/proc/net/route", open_net_route, is_proc },
+        { "net/route", open_net_route, is_proc },
 #endif
 #if defined(TARGET_SPARC) || defined(TARGET_HPPA)
-        { "/proc/cpuinfo", open_cpuinfo, is_proc },
+        { "cpuinfo", open_cpuinfo, is_proc },
 #endif
 #if defined(TARGET_M68K)
-        { "/proc/hardware", open_hardware, is_proc },
+        { "hardware", open_hardware, is_proc },
 #endif
         { NULL, NULL, NULL }
     };
 
-    if (is_proc_myself(pathname, "exe")) {
+    if (is_proc_myself_exe(pathname)) {
         return execfd;
     }
 
@@ -9603,7 +9603,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
             } else if (!arg3) {
                 /* Short circuit this for the magic exe check. */
                 ret = -TARGET_EINVAL;
-            } else if (is_proc_myself((const char *)p, "exe")) {
+            } else if (is_proc_myself_exe((const char *)p)) {
                 char real[PATH_MAX], *temp;
                 temp = realpath(exec_path, real);
                 /* Return value is # of bytes that we wrote to the buffer. */
@@ -9632,7 +9632,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
             p2 = lock_user(VERIFY_WRITE, arg3, arg4, 0);
             if (!p || !p2) {
                 ret = -TARGET_EFAULT;
-            } else if (is_proc_myself((const char *)p, "exe")) {
+            } else if (is_proc_myself_exe((const char *)p)) {
                 char real[PATH_MAX], *temp;
                 temp = realpath(exec_path, real);
                 ret = temp == NULL ? get_errno(-1) : strlen(real) ;
diff --git a/linux-user/syscall_proc.c b/linux-user/syscall_proc.c
index 34051a8..3cf986b 100644
--- a/linux-user/syscall_proc.c
+++ b/linux-user/syscall_proc.c
@@ -1,32 +1,103 @@
 #include "qemu/osdep.h"
+#include "elf.h"
 
 #include "syscall_proc.h"
+#include "qemu.h"
 
-int is_proc_myself(const char *filename, const char *entry)
+#define PROC "/proc/"
+#define SELF "self/"
+#define FD   "fd/"
+
+#define STARTS_WITH(path, CONSTANT) (              \
+    strlen(path) >= strlen(CONSTANT) &&            \
+    strncmp(path, CONSTANT, strlen(CONSTANT)) == 0 \
+)
+
+static inline char *scope_to_proc(const char *path)
 {
-    if (!strncmp(filename, "/proc/", strlen("/proc/"))) {
-        filename += strlen("/proc/");
-        if (!strncmp(filename, "self/", strlen("self/"))) {
-            filename += strlen("self/");
-        } else if (*filename >= '1' && *filename <= '9') {
-            char myself[80];
-            snprintf(myself, sizeof(myself), "%d/", getpid());
-            if (!strncmp(filename, myself, strlen(myself))) {
-                filename += strlen(myself);
-            } else {
-                return 0;
-            }
-        } else {
-            return 0;
-        }
-        if (!strcmp(filename, entry)) {
-            return 1;
+    if (STARTS_WITH(path, PROC)) {
+        return (char *)path + strlen(PROC);
+    }
+
+    return NULL;
+}
+
+static inline char *scope_to_proc_myself(const char *path)
+{
+    char *scope_path = scope_to_proc(path);
+    if (scope_path == NULL) {
+        return NULL;
+    }
+
+    if (STARTS_WITH(scope_path, SELF)) {
+        return scope_path + strlen(SELF);
+    }
+
+    if (strlen(scope_path) >= 1 &&
+        *scope_path >= '1' && *scope_path <= '9') {
+        char pid_path[80];
+        snprintf(pid_path, sizeof(pid_path), "%d/", getpid());
+        if (STARTS_WITH(scope_path, pid_path)) {
+            return scope_path + strlen(pid_path);
         }
     }
-    return 0;
+
+    return NULL;
+}
+
+int is_proc(const char *path, const char *entry)
+{
+    char *scope_path = scope_to_proc(path);
+    if (scope_path == NULL) {
+        return 0;
+    }
+
+    return strcmp(scope_path, entry) == 0;
 }
 
-int is_proc(const char *filename, const char *entry)
+int is_proc_myself(const char *path, const char *entry)
 {
-    return strcmp(filename, entry) == 0;
+    char *scope_path = scope_to_proc_myself(path);
+    if (scope_path == NULL) {
+        return 0;
+    }
+
+    return strcmp(scope_path, entry) == 0;
+}
+
+/*
+ * Kernel creates "fd/#{number}" link after opening "exe" link.
+ * Both "exe" and "fd/#{number}" can be used by application.
+ *
+ * Kernel can provide infinite amount of fd numbers.
+ * QEMU is going to always return single global execfd.
+ *
+ * So we need to check "exe" and "fd/#{execfd}" only.
+ */
+
+int is_proc_myself_exe(const char *path)
+{
+    char *scope_path = scope_to_proc_myself(path);
+    if (scope_path == NULL) {
+        return 0;
+    }
+
+    if (strcmp(scope_path, "exe") == 0) {
+        return 1;
+    }
+
+    if (STARTS_WITH(scope_path, FD)) {
+        scope_path += strlen(FD);
+
+        if (strlen(scope_path) >= 1 &&
+            *scope_path >= '1' && *scope_path <= '9') {
+            char execfd_path[80];
+            snprintf(execfd_path, sizeof(execfd_path), "%d", execfd);
+            if (strcmp(scope_path, execfd_path) == 0) {
+                return 1;
+            }
+        }
+    }
+
+    return 0;
 }
diff --git a/linux-user/syscall_proc.h b/linux-user/syscall_proc.h
index 3098af9..f0e59c0 100644
--- a/linux-user/syscall_proc.h
+++ b/linux-user/syscall_proc.h
@@ -1,7 +1,8 @@
 #ifndef SYSCALL_PROC_H
 #define SYSCALL_PROC_H
 
-int is_proc(const char *filename, const char *entry);
-int is_proc_myself(const char *filename, const char *entry);
+int is_proc(const char *path, const char *entry);
+int is_proc_myself(const char *path, const char *entry);
+int is_proc_myself_exe(const char *path);
 
 #endif
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 4/4] linux user: added tests for proc myself
  2021-02-25 20:54 [PATCH 1/4] linux user: make execfd global (like exec path) and keep it open aladjev.andrew
  2021-02-25 20:54 ` [PATCH 2/4] linux user: moved is proc functions to separate file aladjev.andrew
  2021-02-25 20:54 ` [PATCH 3/4] linux user: refactored is proc myself, added support for fd/<execfd> aladjev.andrew
@ 2021-02-25 20:54 ` aladjev.andrew
  2 siblings, 0 replies; 4+ messages in thread
From: aladjev.andrew @ 2021-02-25 20:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: Andrew Aladjev

From: Andrew Aladjev <aladjev.andrew@gmail.com>

---
 tests/tcg/multiarch/linux-test.c | 85 ++++++++++++++++++++++++++++++++
 1 file changed, 85 insertions(+)

diff --git a/tests/tcg/multiarch/linux-test.c b/tests/tcg/multiarch/linux-test.c
index 96bbad5..4918d45 100644
--- a/tests/tcg/multiarch/linux-test.c
+++ b/tests/tcg/multiarch/linux-test.c
@@ -517,6 +517,89 @@ static void test_shm(void)
     chk_error(shmdt(ptr));
 }
 
+static void test_proc_myself_file(void)
+{
+    int fd1, fd2;
+    char link1[PATH_MAX], link2[PATH_MAX];
+    char buf1[PATH_MAX], buf2[PATH_MAX];
+    int buf1_length, buf2_length;
+
+    /* We can open any file that will always exist. */
+    const char *file_path = "/proc/self/comm";
+
+    char file_realpath[PATH_MAX];
+    if (realpath(file_path, file_realpath) == NULL) {
+        error("proc myself: invalid file");
+    }
+
+    fd1 = chk_error(open(file_path, O_RDONLY));
+    sprintf(link1, "/proc/self/fd/%d", fd1);
+
+    /* Lets try to open same file by first link. */
+    fd2 = chk_error(open(link1, O_RDONLY));
+    sprintf(link2, "/proc/self/fd/%d", fd2);
+
+    /* Two links should point to the same file path. */
+    buf1_length = chk_error(readlink(link1, buf1, PATH_MAX));
+    if (strlen(file_realpath) != buf1_length ||
+        strncmp(file_realpath, buf1, buf1_length) != 0) {
+        error("proc myself: invalid link");
+    }
+    buf2_length = chk_error(readlink(link2, buf2, PATH_MAX));
+    if (strlen(file_realpath) != buf2_length ||
+        strncmp(file_realpath, buf2, buf2_length) != 0) {
+        error("proc myself: invalid link");
+    }
+
+    /* We should be able to read same data from each fd. */
+    buf1_length = chk_error(read(fd1, buf1, PATH_MAX));
+    buf2_length = chk_error(read(fd2, buf2, PATH_MAX));
+    if (buf1_length == 0 || buf1_length != buf2_length ||
+        strncmp(buf1, buf2, buf2_length) != 0) {
+        error("proc myself: invalid file content");
+    }
+
+    chk_error(close(fd2));
+    chk_error(close(fd1));
+}
+
+static void test_proc_myself_exe(void)
+{
+    int fd1, fd2;
+    char link1[PATH_MAX], link2[PATH_MAX];
+    char buf1[PATH_MAX], buf2[PATH_MAX];
+    int buf1_length, buf2_length;
+
+    const char *exe_path = "/proc/self/exe";
+
+    char exe_realpath[PATH_MAX];
+    if (realpath(exe_path, exe_realpath) == NULL) {
+        error("proc myself: invalid exe");
+    }
+
+    fd1 = chk_error(open(exe_path, O_RDONLY));
+    sprintf(link1, "/proc/self/fd/%d", fd1);
+
+    /* Lets try to open link once again. */
+    fd2 = chk_error(open(link1, O_RDONLY));
+    sprintf(link2, "/proc/self/fd/%d", fd2);
+
+    /* Two links should point to the same exe path. */
+    buf1_length = chk_error(readlink(link1, buf1, PATH_MAX));
+    if (strlen(exe_realpath) != buf1_length ||
+        strncmp(exe_realpath, buf1, buf1_length) != 0) {
+        error("proc myself: invalid link");
+    }
+    buf2_length = chk_error(readlink(link2, buf2, PATH_MAX));
+    if (strlen(exe_realpath) != buf2_length ||
+        strncmp(exe_realpath, buf2, buf2_length) != 0) {
+        error("proc myself: invalid link");
+    }
+
+    chk_error(close(fd2));
+    chk_error(close(fd1));
+}
+
 int main(int argc, char **argv)
 {
     test_file();
@@ -532,5 +615,7 @@ int main(int argc, char **argv)
 
     test_signal();
     test_shm();
+    test_proc_myself_file();
+    test_proc_myself_exe();
     return 0;
 }
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-02-25 20:59 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-02-25 20:54 [PATCH 1/4] linux user: make execfd global (like exec path) and keep it open aladjev.andrew
2021-02-25 20:54 ` [PATCH 2/4] linux user: moved is proc functions to separate file aladjev.andrew
2021-02-25 20:54 ` [PATCH 3/4] linux user: refactored is proc myself, added support for fd/<execfd> aladjev.andrew
2021-02-25 20:54 ` [PATCH 4/4] linux user: added tests for proc myself aladjev.andrew

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.