From 2466119c9551d606a0f92f9832e0c865bc04b488 Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:25 +0100 Subject: linux-user: Check array bounds in errno conversion Check array bounds in host_to_target_errno() and target_to_host_errno(). Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-2-git-send-email-T.E.Baldwin99@members.leeds.ac.uk [PMM: Add a lower-bound check, use braces on if(), tweak commit message] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio Reviewed-by: Laurent Vivier --- linux-user/syscall.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 032d338869..5246f360df 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -619,15 +619,19 @@ static uint16_t host_to_target_errno_table[ERRNO_TABLE_SIZE] = { static inline int host_to_target_errno(int err) { - if(host_to_target_errno_table[err]) + if (err >= 0 && err < ERRNO_TABLE_SIZE && + host_to_target_errno_table[err]) { return host_to_target_errno_table[err]; + } return err; } static inline int target_to_host_errno(int err) { - if (target_to_host_errno_table[err]) + if (err >= 0 && err < ERRNO_TABLE_SIZE && + target_to_host_errno_table[err]) { return target_to_host_errno_table[err]; + } return err; } -- cgit v1.2.3 From a3ca7bb2592010eedca31abd11d3ab451cf3b738 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 May 2016 18:47:26 +0100 Subject: linux-user: Consistently return host errnos from do_openat() The function do_openat() is not consistent about whether it is returning a host errno or a guest errno in case of failure. Standardise on returning -1 with errno set (ie caller has to call get_errno()). Signed-off-by: Peter Maydell Reported-by: Timothy Edward Baldwin Signed-off-by: Riku Voipio Reviewed-by: Laurent Vivier --- linux-user/syscall.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 5246f360df..f4c2e190f8 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5559,7 +5559,9 @@ static int open_self_cmdline(void *cpu_env, int fd) nb_read = read(fd_orig, buf, sizeof(buf)); if (nb_read < 0) { + int e = errno; fd_orig = close(fd_orig); + errno = e; return -1; } else if (nb_read == 0) { break; @@ -5579,7 +5581,9 @@ static int open_self_cmdline(void *cpu_env, int fd) if (word_skipped) { if (write(fd, cp_buf, nb_read) != nb_read) { + int e = errno; close(fd_orig); + errno = e; return -1; } } @@ -5599,7 +5603,7 @@ static int open_self_maps(void *cpu_env, int fd) fp = fopen("/proc/self/maps", "r"); if (fp == NULL) { - return -EACCES; + return -1; } while ((read = getline(&line, &len, fp)) != -1) { @@ -5743,7 +5747,7 @@ static int open_net_route(void *cpu_env, int fd) fp = fopen("/proc/net/route", "r"); if (fp == NULL) { - return -EACCES; + return -1; } /* read header */ @@ -5793,7 +5797,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 : get_errno(sys_openat(dirfd, exec_path, flags, mode)); + return execfd ? execfd : sys_openat(dirfd, exec_path, flags, mode); } for (fake_open = fakes; fake_open->filename; fake_open++) { @@ -5819,7 +5823,9 @@ static int do_openat(void *cpu_env, int dirfd, const char *pathname, int flags, unlink(filename); if ((r = fake_open->fill(cpu_env, fd))) { + int e = errno; close(fd); + errno = e; return r; } lseek(fd, 0, SEEK_SET); @@ -5827,7 +5833,7 @@ static int do_openat(void *cpu_env, int dirfd, const char *pathname, int flags, return fd; } - return get_errno(sys_openat(dirfd, path(pathname), flags, mode)); + return sys_openat(dirfd, path(pathname), flags, mode); } #define TIMER_MAGIC 0x0caf0000 -- cgit v1.2.3 From 0284b03ba3f47da53b6b46293a3d586c08829f7e Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:30 +0100 Subject: linux-user: Support for restarting system calls for x86 targets Update the x86 main loop and sigreturn code: * on TARGET_ERESTARTSYS, wind guest PC backwards to repeat syscall insn * set all guest CPU state within signal.c code rather than passing it back out as the "return code" from do_sigreturn() * handle TARGET_QEMU_ESIGRETURN in the main loop as the indication that the main loop should not touch EAX Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-5-git-send-email-T.E.Baldwin99@members.leeds.ac.uk Reviewed-by: Peter Maydell [PMM: Commit message tweaks; drop TARGET_USE_ERESTARTSYS define] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index f4c2e190f8..a4a1af75f6 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6940,12 +6940,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; #ifdef TARGET_NR_sigreturn case TARGET_NR_sigreturn: - /* NOTE: ret is eax, so not transcoding must be done */ ret = do_sigreturn(cpu_env); break; #endif case TARGET_NR_rt_sigreturn: - /* NOTE: ret is eax, so not transcoding must be done */ ret = do_rt_sigreturn(cpu_env); break; case TARGET_NR_sethostname: -- cgit v1.2.3 From 71a8f7fece3e42dc55e865e081866f62f5c8c07e Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:45 +0100 Subject: linux-user: Add debug code to exercise restarting system calls If DEBUG_ERESTARTSYS is set restart all system calls once. This is pure debug code for exercising the syscall restart code paths in the per-architecture cpu main loops. Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-10-git-send-email-T.E.Baldwin99@members.leeds.ac.uk [PMM: Add comment and a commented-out #define next to the commented-out generic DEBUG #define; remove the check on TARGET_USE_ERESTARTSYS; tweak comment message] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index a4a1af75f6..ced519d692 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -110,6 +110,10 @@ int __clone2(int (*fn)(void *), void *child_stack_base, CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID) //#define DEBUG +/* Define DEBUG_ERESTARTSYS to force every syscall to be restarted + * once. This exercises the codepaths for restart. + */ +//#define DEBUG_ERESTARTSYS //#include #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct linux_dirent [2]) @@ -5871,6 +5875,21 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, struct statfs stfs; void *p; +#if defined(DEBUG_ERESTARTSYS) + /* Debug-only code for exercising the syscall-restart code paths + * in the per-architecture cpu main loops: restart every syscall + * the guest makes once before letting it through. + */ + { + static int flag; + + flag = !flag; + if (flag) { + return -TARGET_ERESTARTSYS; + } + } +#endif + #ifdef DEBUG gemu_log("syscall %d", num); #endif -- cgit v1.2.3 From 4d330cee37a21aabfc619a1948953559e66951a4 Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:46 +0100 Subject: linux-user: Provide safe_syscall for fixing races between signals and syscalls If a signal is delivered immediately before a blocking system call the handler will only be called after the system call returns, which may be a long time later or never. This is fixed by using a function (safe_syscall) that checks if a guest signal is pending prior to making a system call, and if so does not call the system call and returns -TARGET_ERESTARTSYS. If a signal is received between the check and the system call host_signal_handler() rewinds execution to before the check. This rewinding has the effect of closing the race window so that safe_syscall will reliably either (a) go into the host syscall with no unprocessed guest signals pending or or (b) return -TARGET_ERESTARTSYS so that the caller can deal with the signals. Implementing this requires a per-host-architecture assembly language fragment. This will also resolve the mishandling of the SA_RESTART flag where we would restart a host system call and not call the guest signal handler until the syscall finally completed -- syscall restarting now always happens at the guest syscall level so the guest signal handler will run. (The host syscall will never be restarted because if the host kernel rewinds the PC to point at the syscall insn for a restart then our host_signal_handler() will see this and arrange the guest PC rewind.) This commit contains the infrastructure for implementing safe_syscall and the assembly language fragment for x86-64, but does not change any syscalls to use it. Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-14-git-send-email-T.E.Baldwin99@members.leeds.ac.uk [PMM: * Avoid having an architecture if-ladder in configure by putting linux-user/host/$(ARCH) on the include path and including safe-syscall.inc.S from it * Avoid ifdef ladder in signal.c by creating new hostdep.h to hold host-architecture-specific things * Added copyright/license header to safe-syscall.inc.S * Rewrote commit message * Added comments to safe-syscall.inc.S * Changed calling convention of safe_syscall() to match syscall() (returns -1 and host error in errno on failure) * Added a long comment in qemu.h about how to use safe_syscall() to implement guest syscalls. ] RV: squashed Peters "fixup! linux-user: compile on non-x86-64 hosts" patch Signed-off-by: Peter Maydell --- linux-user/syscall.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index ced519d692..b6a8ed6c7a 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -660,6 +660,53 @@ char *target_strerror(int err) return strerror(target_to_host_errno(err)); } +#define safe_syscall0(type, name) \ +static type safe_##name(void) \ +{ \ + return safe_syscall(__NR_##name); \ +} + +#define safe_syscall1(type, name, type1, arg1) \ +static type safe_##name(type1 arg1) \ +{ \ + return safe_syscall(__NR_##name, arg1); \ +} + +#define safe_syscall2(type, name, type1, arg1, type2, arg2) \ +static type safe_##name(type1 arg1, type2 arg2) \ +{ \ + return safe_syscall(__NR_##name, arg1, arg2); \ +} + +#define safe_syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ +static type safe_##name(type1 arg1, type2 arg2, type3 arg3) \ +{ \ + return safe_syscall(__NR_##name, arg1, arg2, arg3); \ +} + +#define safe_syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4) \ +static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ +{ \ + return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4); \ +} + +#define safe_syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5) \ +static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) \ +{ \ + return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5); \ +} + +#define safe_syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6) \ +static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) \ +{ \ + return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5, arg6); \ +} + static inline int host_to_target_sock_type(int host_type) { int target_type; -- cgit v1.2.3 From 50afd02b841cf0ccbc988a0fa868bbc4ca67c09e Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:47 +0100 Subject: linux-user: Use safe_syscall for read and write system calls Restart read() and write() if signals occur before, or during with SA_RESTART Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-15-git-send-email-T.E.Baldwin99@members.leeds.ac.uk [PMM: Update to new safe_syscall() convention of setting errno] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index b6a8ed6c7a..bee13607be 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -707,6 +707,9 @@ static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5, arg6); \ } +safe_syscall3(ssize_t, read, int, fd, void *, buff, size_t, count) +safe_syscall3(ssize_t, write, int, fd, const void *, buff, size_t, count) + static inline int host_to_target_sock_type(int host_type) { int target_type; @@ -5983,7 +5986,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, else { if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) goto efault; - ret = get_errno(read(arg1, p, arg3)); + ret = get_errno(safe_read(arg1, p, arg3)); if (ret >= 0 && fd_trans_host_to_target_data(arg1)) { ret = fd_trans_host_to_target_data(arg1)(p, ret); @@ -5994,7 +5997,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_write: if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) goto efault; - ret = get_errno(write(arg1, p, arg3)); + ret = get_errno(safe_write(arg1, p, arg3)); unlock_user(p, arg2, 0); break; #ifdef TARGET_NR_open -- cgit v1.2.3 From c10a07387b77b94d8f7233f3b5bb559211d4e49a Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:48 +0100 Subject: linux-user: Use safe_syscall for open and openat system calls Restart open() and openat() if signals occur before, or during with SA_RESTART. Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-17-git-send-email-T.E.Baldwin99@members.leeds.ac.uk [PMM: Adjusted to follow new -1-and-set-errno safe_syscall convention] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index bee13607be..0037ee7dd7 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -359,18 +359,6 @@ static int sys_getcwd1(char *buf, size_t size) return strlen(buf)+1; } -static int sys_openat(int dirfd, const char *pathname, int flags, mode_t mode) -{ - /* - * open(2) has extra parameter 'mode' when called with - * flag O_CREAT. - */ - if ((flags & O_CREAT) != 0) { - return (openat(dirfd, pathname, flags, mode)); - } - return (openat(dirfd, pathname, flags)); -} - #ifdef TARGET_NR_utimensat #ifdef CONFIG_UTIMENSAT static int sys_utimensat(int dirfd, const char *pathname, @@ -709,6 +697,8 @@ static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ safe_syscall3(ssize_t, read, int, fd, void *, buff, size_t, count) safe_syscall3(ssize_t, write, int, fd, const void *, buff, size_t, count) +safe_syscall4(int, openat, int, dirfd, const char *, pathname, \ + int, flags, mode_t, mode) static inline int host_to_target_sock_type(int host_type) { @@ -5851,7 +5841,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 : sys_openat(dirfd, exec_path, flags, mode); + return execfd ? execfd : safe_openat(dirfd, exec_path, flags, mode); } for (fake_open = fakes; fake_open->filename; fake_open++) { @@ -5887,7 +5877,7 @@ static int do_openat(void *cpu_env, int dirfd, const char *pathname, int flags, return fd; } - return sys_openat(dirfd, path(pathname), flags, mode); + return safe_openat(dirfd, path(pathname), flags, mode); } #define TIMER_MAGIC 0x0caf0000 -- cgit v1.2.3 From 4af80a3783950380df85ecca78aea3e3bad2e846 Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:49 +0100 Subject: linux-user: Use safe_syscall for wait system calls Use safe_syscall for waitpid, waitid and wait4 syscalls. Note that this change allows us to implement support for waitid's fifth (rusage) argument in future; for the moment we ignore it as we have done up til now. Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-18-git-send-email-T.E.Baldwin99@members.leeds.ac.uk [PMM: Adjust to new safe_syscall convention. Add fifth waitid syscall argument (which isn't present in the libc interface but is in the syscall ABI)] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 0037ee7dd7..d9f4695624 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -699,6 +699,10 @@ safe_syscall3(ssize_t, read, int, fd, void *, buff, size_t, count) safe_syscall3(ssize_t, write, int, fd, const void *, buff, size_t, count) safe_syscall4(int, openat, int, dirfd, const char *, pathname, \ int, flags, mode_t, mode) +safe_syscall4(pid_t, wait4, pid_t, pid, int *, status, int, options, \ + struct rusage *, rusage) +safe_syscall5(int, waitid, idtype_t, idtype, id_t, id, siginfo_t *, infop, \ + int, options, struct rusage *, rusage) static inline int host_to_target_sock_type(int host_type) { @@ -6037,7 +6041,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_waitpid: { int status; - ret = get_errno(waitpid(arg1, &status, arg3)); + ret = get_errno(safe_wait4(arg1, &status, arg3, 0)); if (!is_error(ret) && arg2 && ret && put_user_s32(host_to_target_waitstatus(status), arg2)) goto efault; @@ -6049,7 +6053,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, { siginfo_t info; info.si_pid = 0; - ret = get_errno(waitid(arg1, arg2, &info, arg4)); + ret = get_errno(safe_waitid(arg1, arg2, &info, arg4, NULL)); if (!is_error(ret) && arg3 && info.si_pid != 0) { if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_siginfo_t), 0))) goto efault; @@ -7761,7 +7765,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, rusage_ptr = &rusage; else rusage_ptr = NULL; - ret = get_errno(wait4(arg1, &status, arg3, rusage_ptr)); + ret = get_errno(safe_wait4(arg1, &status, arg3, rusage_ptr)); if (!is_error(ret)) { if (status_ptr && ret) { status = host_to_target_waitstatus(status); -- cgit v1.2.3 From ffdcbe223d23461669869e85786145cce65e1e8c Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:50 +0100 Subject: linux-user: Use safe_syscall for execve syscall Wrap execve() in the safe-syscall handling. Although execve() is not an interruptible syscall, it is a special case: 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. The practical upshot is that without this SIGTERM could fail to terminate the process. Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-25-git-send-email-T.E.Baldwin99@members.leeds.ac.uk [PMM: expanded commit message to explain in more detail why this is needed, and add comment about it too] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index d9f4695624..dea827fe04 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -703,6 +703,7 @@ safe_syscall4(pid_t, wait4, pid_t, pid, int *, status, int, options, \ struct rusage *, rusage) safe_syscall5(int, waitid, idtype_t, idtype, id_t, id, siginfo_t *, infop, \ int, options, struct rusage *, rusage) +safe_syscall3(int, execve, const char *, filename, char **, argv, char **, envp) static inline int host_to_target_sock_type(int host_type) { @@ -6179,7 +6180,17 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!(p = lock_user_string(arg1))) goto execve_efault; - ret = get_errno(execve(p, argp, envp)); + /* 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. + */ + ret = get_errno(safe_execve(p, argp, envp)); unlock_user(p, arg1, 0); goto execve_end; -- cgit v1.2.3 From 6df9d38d33be8397bda8838dea5a4a10d662749b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 May 2016 18:47:51 +0100 Subject: linux-user: Use safe_syscall for pselect, select syscalls Use the safe_syscall wrapper for the pselect and select syscalls. Since not every architecture has the select syscall, we now have to implement select in terms of pselect, which means doing timeval<->timespec conversion. (Five years on from the initial patch that added pselect support to QEMU and a decade after pselect6 went into the kernel, it seems safe to not try to support hosts with header files which don't define __NR_pselect6.) Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index dea827fe04..c9c2ae9903 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -430,15 +430,6 @@ _syscall5(int, sys_ppoll, struct pollfd *, fds, nfds_t, nfds, size_t, sigsetsize) #endif -#if defined(TARGET_NR_pselect6) -#ifndef __NR_pselect6 -# define __NR_pselect6 -1 -#endif -#define __NR_sys_pselect6 __NR_pselect6 -_syscall6(int, sys_pselect6, int, nfds, fd_set *, readfds, fd_set *, writefds, - fd_set *, exceptfds, struct timespec *, timeout, void *, sig); -#endif - #if defined(TARGET_NR_prlimit64) #ifndef __NR_prlimit64 # define __NR_prlimit64 -1 @@ -704,6 +695,8 @@ safe_syscall4(pid_t, wait4, pid_t, pid, int *, status, int, options, \ safe_syscall5(int, waitid, idtype_t, idtype, id_t, id, siginfo_t *, infop, \ int, options, struct rusage *, rusage) safe_syscall3(int, execve, const char *, filename, char **, argv, char **, envp) +safe_syscall6(int, pselect6, int, nfds, fd_set *, readfds, fd_set *, writefds, \ + fd_set *, exceptfds, struct timespec *, timeout, void *, sig) static inline int host_to_target_sock_type(int host_type) { @@ -1115,7 +1108,8 @@ static abi_long do_select(int n, { fd_set rfds, wfds, efds; fd_set *rfds_ptr, *wfds_ptr, *efds_ptr; - struct timeval tv, *tv_ptr; + struct timeval tv; + struct timespec ts, *ts_ptr; abi_long ret; ret = copy_from_user_fdset_ptr(&rfds, &rfds_ptr, rfd_addr, n); @@ -1134,12 +1128,15 @@ static abi_long do_select(int n, if (target_tv_addr) { if (copy_from_user_timeval(&tv, target_tv_addr)) return -TARGET_EFAULT; - tv_ptr = &tv; + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; + ts_ptr = &ts; } else { - tv_ptr = NULL; + ts_ptr = NULL; } - ret = get_errno(select(n, rfds_ptr, wfds_ptr, efds_ptr, tv_ptr)); + ret = get_errno(safe_pselect6(n, rfds_ptr, wfds_ptr, efds_ptr, + ts_ptr, NULL)); if (!is_error(ret)) { if (rfd_addr && copy_to_user_fdset(rfd_addr, &rfds, n)) @@ -1149,8 +1146,13 @@ static abi_long do_select(int n, if (efd_addr && copy_to_user_fdset(efd_addr, &efds, n)) return -TARGET_EFAULT; - if (target_tv_addr && copy_to_user_timeval(target_tv_addr, &tv)) - return -TARGET_EFAULT; + if (target_tv_addr) { + tv.tv_sec = ts.tv_sec; + tv.tv_usec = ts.tv_nsec / 1000; + if (copy_to_user_timeval(target_tv_addr, &tv)) { + return -TARGET_EFAULT; + } + } } return ret; @@ -7206,8 +7208,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, sig_ptr = NULL; } - ret = get_errno(sys_pselect6(n, rfds_ptr, wfds_ptr, efds_ptr, - ts_ptr, sig_ptr)); + ret = get_errno(safe_pselect6(n, rfds_ptr, wfds_ptr, efds_ptr, + ts_ptr, sig_ptr)); if (!is_error(ret)) { if (rfd_addr && copy_to_user_fdset(rfd_addr, &rfds, n)) -- cgit v1.2.3 From d509eeb13c9c6fef4a29ca43c64f591d8c61d201 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 May 2016 18:47:52 +0100 Subject: linux-user: Use safe_syscall for futex syscall Use the safe_syscall wrapper for the futex syscall. In particular, this fixes hangs when using programs that link against the Boehm garbage collector, including the Mono runtime. (We don't change the sys_futex() call in the implementation of the exit syscall, because as the FIXME comment there notes that should be handled by disabling signals, since we can't easily back out if the futex were to return ERESTARTSYS.) Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index c9c2ae9903..4e419fb18a 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -697,6 +697,8 @@ safe_syscall5(int, waitid, idtype_t, idtype, id_t, id, siginfo_t *, infop, \ safe_syscall3(int, execve, const char *, filename, char **, argv, char **, envp) safe_syscall6(int, pselect6, int, nfds, fd_set *, readfds, fd_set *, writefds, \ fd_set *, exceptfds, struct timespec *, timeout, void *, sig) +safe_syscall6(int,futex,int *,uaddr,int,op,int,val, \ + const struct timespec *,timeout,int *,uaddr2,int,val3) static inline int host_to_target_sock_type(int host_type) { @@ -5381,12 +5383,12 @@ static int do_futex(target_ulong uaddr, int op, int val, target_ulong timeout, } else { pts = NULL; } - return get_errno(sys_futex(g2h(uaddr), op, tswap32(val), + return get_errno(safe_futex(g2h(uaddr), op, tswap32(val), pts, NULL, val3)); case FUTEX_WAKE: - return get_errno(sys_futex(g2h(uaddr), op, val, NULL, NULL, 0)); + return get_errno(safe_futex(g2h(uaddr), op, val, NULL, NULL, 0)); case FUTEX_FD: - return get_errno(sys_futex(g2h(uaddr), op, val, NULL, NULL, 0)); + return get_errno(safe_futex(g2h(uaddr), op, val, NULL, NULL, 0)); case FUTEX_REQUEUE: case FUTEX_CMP_REQUEUE: case FUTEX_WAKE_OP: @@ -5396,11 +5398,11 @@ static int do_futex(target_ulong uaddr, int op, int val, target_ulong timeout, to satisfy the compiler. We do not need to tswap TIMEOUT since it's not compared to guest memory. */ pts = (struct timespec *)(uintptr_t) timeout; - return get_errno(sys_futex(g2h(uaddr), op, val, pts, - g2h(uaddr2), - (base_op == FUTEX_CMP_REQUEUE - ? tswap32(val3) - : val3))); + return get_errno(safe_futex(g2h(uaddr), op, val, pts, + g2h(uaddr2), + (base_op == FUTEX_CMP_REQUEUE + ? tswap32(val3) + : val3))); default: return -TARGET_ENOSYS; } -- cgit v1.2.3 From c7e35da348e2e4df072e6979c48fa5283e07d1db Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 19 May 2016 12:01:40 +0100 Subject: linux-user: Handle negative values in timespec conversion In a struct timespec, both fields are signed longs. Converting them from guest to host with code like host_ts->tv_sec = tswapal(target_ts->tv_sec); mishandles negative values if the guest has 32-bit longs and the host has 64-bit longs because tswapal()'s return type is abi_ulong: the assignment will zero-extend into the host long type rather than sign-extending it. Make the conversion routines use __get_user() and __set_user() instead: this automatically picks up the signedness of the field type and does the correct kind of sign or zero extension. It also handles the possibility that the target struct is not sufficiently aligned for the host's requirements. In particular, this fixes a hang when running the Linux Test Project mq_timedsend01 and mq_timedreceive01 tests: one of the test cases sets the timeout to -1 and expects an EINVAL failure, but we were setting a very long timeout instead. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 4e419fb18a..6c4f5c6907 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5194,8 +5194,8 @@ static inline abi_long target_to_host_timespec(struct timespec *host_ts, if (!lock_user_struct(VERIFY_READ, target_ts, target_addr, 1)) return -TARGET_EFAULT; - host_ts->tv_sec = tswapal(target_ts->tv_sec); - host_ts->tv_nsec = tswapal(target_ts->tv_nsec); + __get_user(host_ts->tv_sec, &target_ts->tv_sec); + __get_user(host_ts->tv_nsec, &target_ts->tv_nsec); unlock_user_struct(target_ts, target_addr, 0); return 0; } @@ -5207,8 +5207,8 @@ static inline abi_long host_to_target_timespec(abi_ulong target_addr, if (!lock_user_struct(VERIFY_WRITE, target_ts, target_addr, 0)) return -TARGET_EFAULT; - target_ts->tv_sec = tswapal(host_ts->tv_sec); - target_ts->tv_nsec = tswapal(host_ts->tv_nsec); + __put_user(host_ts->tv_sec, &target_ts->tv_sec); + __put_user(host_ts->tv_nsec, &target_ts->tv_nsec); unlock_user_struct(target_ts, target_addr, 1); return 0; } -- cgit v1.2.3 From 99874f65526ed7827202c6e17c62f30d47652bdd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 May 2016 19:00:56 +0100 Subject: linux-user: Handle msgrcv error case correctly The msgrcv ABI is a bit odd -- the msgsz argument is a size_t, which is unsigned, but it must fail EINVAL if the value is negative when cast to a long. We were incorrectly passing the value through an "unsigned int", which meant that if the guest was 32-bit longs and the host was 64-bit longs an input of 0xffffffff (which should trigger EINVAL) would simply be passed to the host msgrcv() as 0xffffffff, where it does not cause the host kernel to reject it. Follow the same approach as do_msgsnd() in using a ssize_t and doing the check for negative values by hand, so we correctly fail in this corner case. This fixes the msgrcv03 Linux Test Project test case, which otherwise hangs. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 6c4f5c6907..cec5b80331 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -3152,7 +3152,7 @@ static inline abi_long do_msgsnd(int msqid, abi_long msgp, } static inline abi_long do_msgrcv(int msqid, abi_long msgp, - unsigned int msgsz, abi_long msgtyp, + ssize_t msgsz, abi_long msgtyp, int msgflg) { struct target_msgbuf *target_mb; @@ -3160,6 +3160,10 @@ static inline abi_long do_msgrcv(int msqid, abi_long msgp, struct msgbuf *host_mb; abi_long ret = 0; + if (msgsz < 0) { + return -TARGET_EINVAL; + } + if (!lock_user_struct(VERIFY_WRITE, target_mb, msgp, 0)) return -TARGET_EFAULT; -- cgit v1.2.3 From 415d847110e3f8cd176160b92a5fdc56d8a20792 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 May 2016 19:00:57 +0100 Subject: linux-user: Use g_try_malloc() in do_msgrcv() In do_msgrcv() we want to allocate a message buffer, whose size is passed to us by the guest. That means we could legitimately fail, so use g_try_malloc() and handle the error case, in the same way that do_msgsnd() does. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index cec5b80331..40e8742924 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -3167,7 +3167,11 @@ static inline abi_long do_msgrcv(int msqid, abi_long msgp, if (!lock_user_struct(VERIFY_WRITE, target_mb, msgp, 0)) return -TARGET_EFAULT; - host_mb = g_malloc(msgsz+sizeof(long)); + host_mb = g_try_malloc(msgsz + sizeof(long)); + if (!host_mb) { + ret = -TARGET_ENOMEM; + goto end; + } ret = get_errno(msgrcv(msqid, host_mb, msgsz, msgtyp, msgflg)); if (ret > 0) { -- cgit v1.2.3 From fd6f7798ac3066ad9e3956defd37521830197666 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 1 Mar 2016 16:33:02 +0000 Subject: linux-user: Use direct syscalls for setuid(), etc On Linux the setuid(), setgid(), etc system calls have different semantics from the libc functions. The libc functions follow POSIX and update the credentials for all threads in the process; the system calls update only the thread which makes the call. (This impedance mismatch is worked around in libc by signalling all threads to tell them to do a syscall, in a byzantine and fragile way; see http://ewontfix.com/17/.) Since in linux-user we are trying to emulate the system call semantics, we must implement all these syscalls to directly call the underlying host syscall, rather than calling the host libc function. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 58 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 12 deletions(-) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 40e8742924..df70255e5f 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5099,6 +5099,40 @@ static inline int tswapid(int id) #endif /* USE_UID16 */ +/* We must do direct syscalls for setting UID/GID, because we want to + * implement the Linux system call semantics of "change only for this thread", + * not the libc/POSIX semantics of "change for all threads in process". + * (See http://ewontfix.com/17/ for more details.) + * We use the 32-bit version of the syscalls if present; if it is not + * then either the host architecture supports 32-bit UIDs natively with + * the standard syscall, or the 16-bit UID is the best we can do. + */ +#ifdef __NR_setuid32 +#define __NR_sys_setuid __NR_setuid32 +#else +#define __NR_sys_setuid __NR_setuid +#endif +#ifdef __NR_setgid32 +#define __NR_sys_setgid __NR_setgid32 +#else +#define __NR_sys_setgid __NR_setgid +#endif +#ifdef __NR_setresuid32 +#define __NR_sys_setresuid __NR_setresuid32 +#else +#define __NR_sys_setresuid __NR_setresuid +#endif +#ifdef __NR_setresgid32 +#define __NR_sys_setresgid __NR_setresgid32 +#else +#define __NR_sys_setresgid __NR_setresgid +#endif + +_syscall1(int, sys_setuid, uid_t, uid) +_syscall1(int, sys_setgid, gid_t, gid) +_syscall3(int, sys_setresuid, uid_t, ruid, uid_t, euid, uid_t, suid) +_syscall3(int, sys_setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid) + void syscall_init(void) { IOCTLEntry *ie; @@ -8834,9 +8868,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_setresuid case TARGET_NR_setresuid: - ret = get_errno(setresuid(low2highuid(arg1), - low2highuid(arg2), - low2highuid(arg3))); + ret = get_errno(sys_setresuid(low2highuid(arg1), + low2highuid(arg2), + low2highuid(arg3))); break; #endif #ifdef TARGET_NR_getresuid @@ -8855,9 +8889,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_getresgid case TARGET_NR_setresgid: - ret = get_errno(setresgid(low2highgid(arg1), - low2highgid(arg2), - low2highgid(arg3))); + ret = get_errno(sys_setresgid(low2highgid(arg1), + low2highgid(arg2), + low2highgid(arg3))); break; #endif #ifdef TARGET_NR_getresgid @@ -8883,10 +8917,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; #endif case TARGET_NR_setuid: - ret = get_errno(setuid(low2highuid(arg1))); + ret = get_errno(sys_setuid(low2highuid(arg1))); break; case TARGET_NR_setgid: - ret = get_errno(setgid(low2highgid(arg1))); + ret = get_errno(sys_setgid(low2highgid(arg1))); break; case TARGET_NR_setfsuid: ret = get_errno(setfsuid(arg1)); @@ -9168,7 +9202,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_setresuid32 case TARGET_NR_setresuid32: - ret = get_errno(setresuid(arg1, arg2, arg3)); + ret = get_errno(sys_setresuid(arg1, arg2, arg3)); break; #endif #ifdef TARGET_NR_getresuid32 @@ -9187,7 +9221,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_setresgid32 case TARGET_NR_setresgid32: - ret = get_errno(setresgid(arg1, arg2, arg3)); + ret = get_errno(sys_setresgid(arg1, arg2, arg3)); break; #endif #ifdef TARGET_NR_getresgid32 @@ -9214,12 +9248,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_setuid32 case TARGET_NR_setuid32: - ret = get_errno(setuid(arg1)); + ret = get_errno(sys_setuid(arg1)); break; #endif #ifdef TARGET_NR_setgid32 case TARGET_NR_setgid32: - ret = get_errno(setgid(arg1)); + ret = get_errno(sys_setgid(arg1)); break; #endif #ifdef TARGET_NR_setfsuid32 -- cgit v1.2.3