bsd-user: Implement freebsd_exec_common, used in implementing execve/fexecve.

Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Karim Taha <kariem.taha2.7@gmail.com>
Reviewed-by: Warner Losh <imp@bsdimp.com>
Message-Id: <20230925182425.3163-21-kariem.taha2.7@gmail.com>
This commit is contained in:
Stacey Son 2023-09-25 21:24:17 +03:00 committed by Warner Losh
parent 84d41c5e6d
commit 8632729060
3 changed files with 180 additions and 4 deletions

View File

@ -30,9 +30,7 @@ struct kinfo_proc;
* Get the filename for the given file descriptor. * Get the filename for the given file descriptor.
* Note that this may return NULL (fail) if no longer cached in the kernel. * Note that this may return NULL (fail) if no longer cached in the kernel.
*/ */
char * static char *
get_filename_from_fd(pid_t pid, int fd, char *filename, size_t len);
char *
get_filename_from_fd(pid_t pid, int fd, char *filename, size_t len) get_filename_from_fd(pid_t pid, int fd, char *filename, size_t len)
{ {
char *ret = NULL; char *ret = NULL;
@ -80,3 +78,180 @@ out:
return ret; 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;
}

View File

@ -88,7 +88,7 @@ unsigned long reserved_va = MAX_RESERVED_VA;
unsigned long reserved_va; unsigned long reserved_va;
#endif #endif
static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
const char *qemu_uname_release; const char *qemu_uname_release;
char qemu_proc_pathname[PATH_MAX]; /* full path to exeutable */ char qemu_proc_pathname[PATH_MAX]; /* full path to exeutable */

View File

@ -111,6 +111,7 @@ typedef struct TaskState {
} __attribute__((aligned(16))) TaskState; } __attribute__((aligned(16))) TaskState;
void stop_all_tasks(void); void stop_all_tasks(void);
extern const char *interp_prefix;
extern const char *qemu_uname_release; extern const char *qemu_uname_release;
/* /*