diff mbox series

[v2,20/28] bsd-user: Implement freebsd_exec_common, used in implementing execve/fexecve.

Message ID 20230917213803.20683-21-kariem.taha2.7@gmail.com
State New
Headers show
Series bsd-user: Implement freebsd process related system calls. | expand

Commit Message

Karim Taha Sept. 17, 2023, 9:37 p.m. UTC
From: Stacey Son <sson@FreeBSD.org>

Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Karim Taha <kariem.taha2.7@gmail.com>
---
 bsd-user/freebsd/os-proc.c | 177 +++++++++++++++++++++++++++++++++++++
 bsd-user/main.c            |   2 +-
 bsd-user/qemu.h            |   1 +
 3 files changed, 179 insertions(+), 1 deletion(-)

Comments

Warner Losh Sept. 20, 2023, 6:57 p.m. UTC | #1
On Sun, Sep 17, 2023 at 10:39 PM Karim Taha <kariem.taha2.7@gmail.com>
wrote:

> From: Stacey Son <sson@FreeBSD.org>
>
> Signed-off-by: Stacey Son <sson@FreeBSD.org>
> Signed-off-by: Karim Taha <kariem.taha2.7@gmail.com>
> ---
>  bsd-user/freebsd/os-proc.c | 177 +++++++++++++++++++++++++++++++++++++
>  bsd-user/main.c            |   2 +-
>  bsd-user/qemu.h            |   1 +
>  3 files changed, 179 insertions(+), 1 deletion(-)
>

Reviewed-by: Warner Losh <imp@bsdimp.com>

But see comment below.


> diff --git a/bsd-user/freebsd/os-proc.c b/bsd-user/freebsd/os-proc.c
> index cb35f29f10..12d78b7fc9 100644
> --- a/bsd-user/freebsd/os-proc.c
> +++ b/bsd-user/freebsd/os-proc.c
> @@ -78,3 +78,180 @@ out:
>      return ret;
>  }
>
> +/*
> + * execve/fexecve
> + */
> +abi_long freebsd_exec_common(abi_ulong path_or_fd, abi_ulong guest_argp,
> +        abi_ulong guest_envp, int do_fexec)
> +{
> +    char **argp, **envp, **qargp, **qarg1, **qarg0, **qargend;
> +    int argc, envc;
> +    abi_ulong gp;
> +    abi_ulong addr;
> +    char **q;
> +    int total_size = 0;
> +    void *p;
> +    abi_long ret;
> +
> +    argc = 0;
> +    for (gp = guest_argp; gp; gp += sizeof(abi_ulong)) {
> +        if (get_user_ual(addr, gp)) {
> +            return -TARGET_EFAULT;
> +        }
> +        if (!addr) {
> +            break;
> +        }
> +        argc++;
> +    }
> +    envc = 0;
> +    for (gp = guest_envp; gp; gp += sizeof(abi_ulong)) {
> +        if (get_user_ual(addr, gp)) {
> +            return -TARGET_EFAULT;
> +        }
> +        if (!addr) {
> +            break;
> +        }
> +        envc++;
> +    }
> +
> +    qarg0 = argp = g_new0(char *, argc + 9);
> +    /* save the first agrument for the emulator */
> +    *argp++ = (char *)getprogname();
> +    qargp = argp;
> +    *argp++ = (char *)getprogname();
> +    qarg1 = argp;
> +    envp = g_new0(char *, envc + 1);
> +    for (gp = guest_argp, q = argp; gp; gp += sizeof(abi_ulong), q++) {
> +        if (get_user_ual(addr, gp)) {
> +            ret = -TARGET_EFAULT;
> +            goto execve_end;
> +        }
> +        if (!addr) {
> +            break;
> +        }
> +        *q = lock_user_string(addr);
> +        if (*q == NULL) {
> +            ret = -TARGET_EFAULT;
> +            goto execve_end;
> +        }
> +        total_size += strlen(*q) + 1;
> +    }
> +    *q++ = NULL;
> +    qargend = q;
> +
> +    for (gp = guest_envp, q = envp; gp; gp += sizeof(abi_ulong), q++) {
> +        if (get_user_ual(addr, gp)) {
> +            ret = -TARGET_EFAULT;
> +            goto execve_end;
> +        }
> +        if (!addr) {
> +            break;
> +        }
> +        *q = lock_user_string(addr);
> +        if (*q == NULL) {
> +            ret = -TARGET_EFAULT;
> +            goto execve_end;
> +        }
> +        total_size += strlen(*q) + 1;
> +    }
> +    *q = NULL;
> +
> +    /*
> +     * This case will not be caught by the host's execve() if its
> +     * page size is bigger than the target's.
> +     */
> +    if (total_size > MAX_ARG_PAGES * TARGET_PAGE_SIZE) {
> +        ret = -TARGET_E2BIG;
> +        goto execve_end;
> +    }
> +
> +    if (do_fexec) {
> +        if (((int)path_or_fd > 0 &&
> +            is_target_elf_binary((int)path_or_fd)) == 1) {
> +            char execpath[PATH_MAX];
> +
> +            /*
> +             * The executable is an elf binary for the target
> +             * arch.  execve() it using the emulator if we can
> +             * determine the filename path from the fd.
> +             */
>

So we do this fd dance so we can make things like 'qemu-arm-static
/armv7/bin/sh' work.
Doug Rabson has some changes that means we can ditch this, I think, since
the
kernel will just track it and it will default to 'what is doing the current
process'
rather than the system default for the same binfmt entry.


> +            if (get_filename_from_fd(getpid(), (int)path_or_fd, execpath,
> +                        sizeof(execpath)) != NULL) {
> +                memmove(qarg1 + 2, qarg1, (qargend - qarg1) *
> sizeof(*qarg1));
> +                qarg1[1] = qarg1[0];
> +                qarg1[0] = (char *)"-0";
> +                qarg1 += 2;
> +                qargend += 2;
> +                *qarg1 = execpath;
> +#ifndef DONT_INHERIT_INTERP_PREFIX
> +                memmove(qarg1 + 2, qarg1, (qargend - qarg1) *
> sizeof(*qarg1));
> +                *qarg1++ = (char *)"-L";
> +                *qarg1++ = (char *)interp_prefix;
> +#endif
>

And we do this inheritance so we can pass in a non-standard library path,
maybe for testing, and have the above example also work.

Warner


> +                ret = get_errno(execve(qemu_proc_pathname, qargp, envp));
> +            } else {
> +                /* Getting the filename path failed. */
> +                ret = -TARGET_EBADF;
> +                goto execve_end;
> +            }
> +        } else {
> +            ret = get_errno(fexecve((int)path_or_fd, argp, envp));
> +        }
> +    } else {
> +        int fd;
> +
> +        p = lock_user_string(path_or_fd);
> +        if (p == NULL) {
> +            ret = -TARGET_EFAULT;
> +            goto execve_end;
> +        }
> +
> +        /*
> +         * Check the header and see if it a target elf binary.  If so
> +         * then execute using qemu user mode emulator.
> +         */
> +        fd = open(p, O_RDONLY | O_CLOEXEC);
> +        if (fd > 0 && is_target_elf_binary(fd) == 1) {
> +            close(fd);
> +            /* execve() as a target binary using emulator. */
> +            memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1));
> +            qarg1[1] = qarg1[0];
> +            qarg1[0] = (char *)"-0";
> +            qarg1 += 2;
> +            qargend += 2;
> +            *qarg1 = (char *)p;
> +#ifndef DONT_INHERIT_INTERP_PREFIX
> +            memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1));
> +            *qarg1++ = (char *)"-L";
> +            *qarg1++ = (char *)interp_prefix;
> +#endif
> +            ret = get_errno(execve(qemu_proc_pathname, qargp, envp));
> +        } else {
> +            close(fd);
> +            /* Execve() as a host native binary. */
> +            ret = get_errno(execve(p, argp, envp));
> +        }
> +        unlock_user(p, path_or_fd, 0);
> +    }


It might be nice to unify these two if statements, but I'm thinking
we don't want to do it now since Doug's work will obsolete it if I
understand it correctly.

Warner


>
>
+execve_end:
> +    for (gp = guest_argp, q = argp; *q; gp += sizeof(abi_ulong), q++) {
> +        if (get_user_ual(addr, gp) || !addr) {
> +            break;
> +        }
> +        unlock_user(*q, addr, 0);
> +    }
> +
> +    for (gp = guest_envp, q = envp; *q; gp += sizeof(abi_ulong), q++) {
> +        if (get_user_ual(addr, gp) || !addr) {
> +            break;
> +        }
> +        unlock_user(*q, addr, 0);
> +    }
> +
> +    g_free(qarg0);
> +    g_free(envp);
> +
> +    return ret;
> +}
> +
> diff --git a/bsd-user/main.c b/bsd-user/main.c
> index f913cb55a7..a12b4be80f 100644
> --- a/bsd-user/main.c
> +++ b/bsd-user/main.c
> @@ -88,7 +88,7 @@ unsigned long reserved_va = MAX_RESERVED_VA;
>  unsigned long reserved_va;
>  #endif
>
> -static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
> +const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
>  const char *qemu_uname_release;
>  char qemu_proc_pathname[PATH_MAX];  /* full path to exeutable */
>
> diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
> index 2cf96d9a15..1ed6024b5d 100644
> --- a/bsd-user/qemu.h
> +++ b/bsd-user/qemu.h
> @@ -111,6 +111,7 @@ typedef struct TaskState {
>  } __attribute__((aligned(16))) TaskState;
>
>  void stop_all_tasks(void);
> +extern const char *interp_prefix;
>  extern const char *qemu_uname_release;
>
>  /*
> --
> 2.42.0
>
>
diff mbox series

Patch

diff --git a/bsd-user/freebsd/os-proc.c b/bsd-user/freebsd/os-proc.c
index cb35f29f10..12d78b7fc9 100644
--- a/bsd-user/freebsd/os-proc.c
+++ b/bsd-user/freebsd/os-proc.c
@@ -78,3 +78,180 @@  out:
     return ret;
 }
 
+/*
+ * execve/fexecve
+ */
+abi_long freebsd_exec_common(abi_ulong path_or_fd, abi_ulong guest_argp,
+        abi_ulong guest_envp, int do_fexec)
+{
+    char **argp, **envp, **qargp, **qarg1, **qarg0, **qargend;
+    int argc, envc;
+    abi_ulong gp;
+    abi_ulong addr;
+    char **q;
+    int total_size = 0;
+    void *p;
+    abi_long ret;
+
+    argc = 0;
+    for (gp = guest_argp; gp; gp += sizeof(abi_ulong)) {
+        if (get_user_ual(addr, gp)) {
+            return -TARGET_EFAULT;
+        }
+        if (!addr) {
+            break;
+        }
+        argc++;
+    }
+    envc = 0;
+    for (gp = guest_envp; gp; gp += sizeof(abi_ulong)) {
+        if (get_user_ual(addr, gp)) {
+            return -TARGET_EFAULT;
+        }
+        if (!addr) {
+            break;
+        }
+        envc++;
+    }
+
+    qarg0 = argp = g_new0(char *, argc + 9);
+    /* save the first agrument for the emulator */
+    *argp++ = (char *)getprogname();
+    qargp = argp;
+    *argp++ = (char *)getprogname();
+    qarg1 = argp;
+    envp = g_new0(char *, envc + 1);
+    for (gp = guest_argp, q = argp; gp; gp += sizeof(abi_ulong), q++) {
+        if (get_user_ual(addr, gp)) {
+            ret = -TARGET_EFAULT;
+            goto execve_end;
+        }
+        if (!addr) {
+            break;
+        }
+        *q = lock_user_string(addr);
+        if (*q == NULL) {
+            ret = -TARGET_EFAULT;
+            goto execve_end;
+        }
+        total_size += strlen(*q) + 1;
+    }
+    *q++ = NULL;
+    qargend = q;
+
+    for (gp = guest_envp, q = envp; gp; gp += sizeof(abi_ulong), q++) {
+        if (get_user_ual(addr, gp)) {
+            ret = -TARGET_EFAULT;
+            goto execve_end;
+        }
+        if (!addr) {
+            break;
+        }
+        *q = lock_user_string(addr);
+        if (*q == NULL) {
+            ret = -TARGET_EFAULT;
+            goto execve_end;
+        }
+        total_size += strlen(*q) + 1;
+    }
+    *q = NULL;
+
+    /*
+     * This case will not be caught by the host's execve() if its
+     * page size is bigger than the target's.
+     */
+    if (total_size > MAX_ARG_PAGES * TARGET_PAGE_SIZE) {
+        ret = -TARGET_E2BIG;
+        goto execve_end;
+    }
+
+    if (do_fexec) {
+        if (((int)path_or_fd > 0 &&
+            is_target_elf_binary((int)path_or_fd)) == 1) {
+            char execpath[PATH_MAX];
+
+            /*
+             * The executable is an elf binary for the target
+             * arch.  execve() it using the emulator if we can
+             * determine the filename path from the fd.
+             */
+            if (get_filename_from_fd(getpid(), (int)path_or_fd, execpath,
+                        sizeof(execpath)) != NULL) {
+                memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1));
+                qarg1[1] = qarg1[0];
+                qarg1[0] = (char *)"-0";
+                qarg1 += 2;
+                qargend += 2;
+                *qarg1 = execpath;
+#ifndef DONT_INHERIT_INTERP_PREFIX
+                memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1));
+                *qarg1++ = (char *)"-L";
+                *qarg1++ = (char *)interp_prefix;
+#endif
+                ret = get_errno(execve(qemu_proc_pathname, qargp, envp));
+            } else {
+                /* Getting the filename path failed. */
+                ret = -TARGET_EBADF;
+                goto execve_end;
+            }
+        } else {
+            ret = get_errno(fexecve((int)path_or_fd, argp, envp));
+        }
+    } else {
+        int fd;
+
+        p = lock_user_string(path_or_fd);
+        if (p == NULL) {
+            ret = -TARGET_EFAULT;
+            goto execve_end;
+        }
+
+        /*
+         * Check the header and see if it a target elf binary.  If so
+         * then execute using qemu user mode emulator.
+         */
+        fd = open(p, O_RDONLY | O_CLOEXEC);
+        if (fd > 0 && is_target_elf_binary(fd) == 1) {
+            close(fd);
+            /* execve() as a target binary using emulator. */
+            memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1));
+            qarg1[1] = qarg1[0];
+            qarg1[0] = (char *)"-0";
+            qarg1 += 2;
+            qargend += 2;
+            *qarg1 = (char *)p;
+#ifndef DONT_INHERIT_INTERP_PREFIX
+            memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1));
+            *qarg1++ = (char *)"-L";
+            *qarg1++ = (char *)interp_prefix;
+#endif
+            ret = get_errno(execve(qemu_proc_pathname, qargp, envp));
+        } else {
+            close(fd);
+            /* Execve() as a host native binary. */
+            ret = get_errno(execve(p, argp, envp));
+        }
+        unlock_user(p, path_or_fd, 0);
+    }
+
+execve_end:
+    for (gp = guest_argp, q = argp; *q; gp += sizeof(abi_ulong), q++) {
+        if (get_user_ual(addr, gp) || !addr) {
+            break;
+        }
+        unlock_user(*q, addr, 0);
+    }
+
+    for (gp = guest_envp, q = envp; *q; gp += sizeof(abi_ulong), q++) {
+        if (get_user_ual(addr, gp) || !addr) {
+            break;
+        }
+        unlock_user(*q, addr, 0);
+    }
+
+    g_free(qarg0);
+    g_free(envp);
+
+    return ret;
+}
+
diff --git a/bsd-user/main.c b/bsd-user/main.c
index f913cb55a7..a12b4be80f 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -88,7 +88,7 @@  unsigned long reserved_va = MAX_RESERVED_VA;
 unsigned long reserved_va;
 #endif
 
-static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
+const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
 const char *qemu_uname_release;
 char qemu_proc_pathname[PATH_MAX];  /* full path to exeutable */
 
diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
index 2cf96d9a15..1ed6024b5d 100644
--- a/bsd-user/qemu.h
+++ b/bsd-user/qemu.h
@@ -111,6 +111,7 @@  typedef struct TaskState {
 } __attribute__((aligned(16))) TaskState;
 
 void stop_all_tasks(void);
+extern const char *interp_prefix;
 extern const char *qemu_uname_release;
 
 /*