linux-user/syscall: Extract do_execve() from do_syscall1()

execve() is a particular case of execveat(). In order
to add do_execveat(), first factor do_execve() out.

Signed-off-by: Drew DeVault <sir@cmpwn.com>
Message-Id: <20221104081015.706009-1-sir@cmpwn.com>
[PMD: Split of bigger patch, filled description, fixed style]
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
Message-Id: <20221104173632.1052-5-philmd@linaro.org>
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
This commit is contained in:
Drew DeVault 2022-11-04 18:36:31 +01:00 committed by Laurent Vivier
parent 5667a1aebe
commit 156e1f6718
1 changed files with 114 additions and 97 deletions

View File

@ -8357,6 +8357,119 @@ static int do_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, int
return safe_openat(dirfd, path(pathname), flags, mode);
}
static int do_execve(CPUArchState *cpu_env,
abi_long pathname, abi_long guest_argp,
abi_long guest_envp)
{
int ret;
char **argp, **envp;
int argc, envc;
abi_ulong gp;
abi_ulong addr;
char **q;
void *p;
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++;
}
argp = g_new0(char *, argc + 1);
envp = g_new0(char *, envc + 1);
for (gp = guest_argp, q = argp; gp; gp += sizeof(abi_ulong), q++) {
if (get_user_ual(addr, gp)) {
goto execve_efault;
}
if (!addr) {
break;
}
*q = lock_user_string(addr);
if (!*q) {
goto execve_efault;
}
}
*q = NULL;
for (gp = guest_envp, q = envp; gp; gp += sizeof(abi_ulong), q++) {
if (get_user_ual(addr, gp)) {
goto execve_efault;
}
if (!addr) {
break;
}
*q = lock_user_string(addr);
if (!*q) {
goto execve_efault;
}
}
*q = NULL;
/*
* Although execve() is not an interruptible syscall it is
* a special case where we must use the safe_syscall wrapper:
* if we allow a signal to happen before we make the host
* syscall then we will 'lose' it, because at the point of
* execve the process leaves QEMU's control. So we use the
* safe syscall wrapper to ensure that we either take the
* signal as a guest signal, or else it does not happen
* before the execve completes and makes it the other
* program's problem.
*/
p = lock_user_string(pathname);
if (!p) {
goto execve_efault;
}
if (is_proc_myself(p, "exe")) {
ret = get_errno(safe_execve(exec_path, argp, envp));
} else {
ret = get_errno(safe_execve(p, argp, envp));
}
unlock_user(p, pathname, 0);
goto execve_end;
execve_efault:
ret = -TARGET_EFAULT;
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(argp);
g_free(envp);
return ret;
}
#define TIMER_MAGIC 0x0caf0000
#define TIMER_MAGIC_MASK 0xffff0000
@ -8867,103 +8980,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
return ret;
#endif
case TARGET_NR_execve:
{
char **argp, **envp;
int argc, envc;
abi_ulong gp;
abi_ulong guest_argp;
abi_ulong guest_envp;
abi_ulong addr;
char **q;
argc = 0;
guest_argp = arg2;
for (gp = guest_argp; gp; gp += sizeof(abi_ulong)) {
if (get_user_ual(addr, gp))
return -TARGET_EFAULT;
if (!addr)
break;
argc++;
}
envc = 0;
guest_envp = arg3;
for (gp = guest_envp; gp; gp += sizeof(abi_ulong)) {
if (get_user_ual(addr, gp))
return -TARGET_EFAULT;
if (!addr)
break;
envc++;
}
argp = g_new0(char *, argc + 1);
envp = g_new0(char *, envc + 1);
for (gp = guest_argp, q = argp; gp;
gp += sizeof(abi_ulong), q++) {
if (get_user_ual(addr, gp))
goto execve_efault;
if (!addr)
break;
if (!(*q = lock_user_string(addr)))
goto execve_efault;
}
*q = NULL;
for (gp = guest_envp, q = envp; gp;
gp += sizeof(abi_ulong), q++) {
if (get_user_ual(addr, gp))
goto execve_efault;
if (!addr)
break;
if (!(*q = lock_user_string(addr)))
goto execve_efault;
}
*q = NULL;
if (!(p = lock_user_string(arg1)))
goto execve_efault;
/* Although execve() is not an interruptible syscall it is
* a special case where we must use the safe_syscall wrapper:
* if we allow a signal to happen before we make the host
* syscall then we will 'lose' it, because at the point of
* execve the process leaves QEMU's control. So we use the
* safe syscall wrapper to ensure that we either take the
* signal as a guest signal, or else it does not happen
* before the execve completes and makes it the other
* program's problem.
*/
if (is_proc_myself(p, "exe")) {
ret = get_errno(safe_execve(exec_path, argp, envp));
} else {
ret = get_errno(safe_execve(p, argp, envp));
}
unlock_user(p, arg1, 0);
goto execve_end;
execve_efault:
ret = -TARGET_EFAULT;
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(argp);
g_free(envp);
}
return ret;
return do_execve(cpu_env, arg1, arg2, arg3);
case TARGET_NR_chdir:
if (!(p = lock_user_string(arg1)))
return -TARGET_EFAULT;