From dab32b321f4d510ed5171b12f68bd5aa7a02cffe Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jul 2016 14:57:26 +0100 Subject: [PATCH 01/26] linux-user: Fix handling of iovec counts In the kernel the length of an iovec is generally handled as an unsigned long, not an integer; fix the parameter to lock_iovec() accordingly. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index ca06943f3b..71f40e3ab8 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -3119,7 +3119,7 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, } static struct iovec *lock_iovec(int type, abi_ulong target_addr, - int count, int copy) + abi_ulong count, int copy) { struct target_iovec *target_vec; struct iovec *vec; @@ -3132,7 +3132,7 @@ static struct iovec *lock_iovec(int type, abi_ulong target_addr, errno = 0; return NULL; } - if (count < 0 || count > IOV_MAX) { + if (count > IOV_MAX) { errno = EINVAL; return NULL; } @@ -3207,7 +3207,7 @@ static struct iovec *lock_iovec(int type, abi_ulong target_addr, } static void unlock_iovec(struct iovec *vec, abi_ulong target_addr, - int count, int copy) + abi_ulong count, int copy) { struct target_iovec *target_vec; int i; @@ -3462,7 +3462,7 @@ static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp, { abi_long ret, len; struct msghdr msg; - int count; + abi_ulong count; struct iovec *vec; abi_ulong target_vec; From 97b079703350ec0f6625788fb380f1fa14d0e2c4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jul 2016 14:57:27 +0100 Subject: [PATCH 02/26] linux-user: Fix errno for sendrecvmsg with large iovec length The sendmsg and recvmsg syscalls use a different errno to indicate an overlarge iovec length from readv and writev. Handle this special case in do_sendrcvmsg_locked() to avoid getting the default errno returned by lock_iovec(). Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 71f40e3ab8..9d18326467 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -3485,6 +3485,15 @@ static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp, count = tswapal(msgp->msg_iovlen); target_vec = tswapal(msgp->msg_iov); + + if (count > IOV_MAX) { + /* sendrcvmsg returns a different errno for this condition than + * readv/writev, so we must catch it here before lock_iovec() does. + */ + ret = -TARGET_EMSGSIZE; + goto out2; + } + vec = lock_iovec(send ? VERIFY_READ : VERIFY_WRITE, target_vec, count, send); if (vec == NULL) { From 26a6fc96e0ca7522b855c2164bc6098240c286f6 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jul 2016 14:57:28 +0100 Subject: [PATCH 03/26] linux-user: Allow bad msg_name for recvfrom on connected socket The POSIX standard mandates that for a connected socket recvfrom() must ignore the msg_name and msg_namelen fields. This is awkward for QEMU because we will attempt to copy them from guest address space. Handle this by not immediately returning a TARGET_EFAULT if the copy failed, but instead passing a known-bad address to the host kernel, which can then return EFAULT or ignore the value appropriately. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 9d18326467..51f558d47d 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -3472,7 +3472,14 @@ static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp, ret = target_to_host_sockaddr(fd, msg.msg_name, tswapal(msgp->msg_name), msg.msg_namelen); - if (ret) { + if (ret == -TARGET_EFAULT) { + /* For connected sockets msg_name and msg_namelen must + * be ignored, so returning EFAULT immediately is wrong. + * Instead, pass a bad msg_name to the host kernel, and + * let it decide whether to return EFAULT or not. + */ + msg.msg_name = (void *)-1; + } else if (ret) { goto out2; } } else { @@ -3534,7 +3541,7 @@ static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp, } if (!is_error(ret)) { msgp->msg_namelen = tswap32(msg.msg_namelen); - if (msg.msg_name != NULL) { + if (msg.msg_name != NULL && msg.msg_name != (void *)-1) { ret = host_to_target_sockaddr(tswapal(msgp->msg_name), msg.msg_name, msg.msg_namelen); if (ret) { From 6080723102d1ad3b553769834d6a23e3f3d8250f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Jul 2016 18:44:45 +0100 Subject: [PATCH 04/26] linux-user: Implement FS_IOC_GETFLAGS and FS_IOC_SETFLAGS ioctls Implement the FS_IOC_GETFLAGS and FS_IOC_SETFLAGS ioctls, as used by chattr. Note that the type information encoded in these ioctl numbers is at odds with the actual type the kernel accesses, as discussed in http://thread.gmane.org/gmane.linux.file-systems/80164. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/ioctls.h | 3 +++ linux-user/syscall_defs.h | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 7e2c133ba1..1bad701481 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -120,6 +120,9 @@ MK_PTR(MK_STRUCT(STRUCT_fiemap))) #endif + IOCTL(FS_IOC_GETFLAGS, IOC_R, MK_PTR(TYPE_INT)) + IOCTL(FS_IOC_SETFLAGS, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(SIOCATMARK, IOC_R, MK_PTR(TYPE_INT)) IOCTL(SIOCGIFNAME, IOC_RW, MK_PTR(TYPE_INT)) IOCTL(SIOCGIFFLAGS, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_short_ifreq))) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index c0c9b5887c..c0e5cb0010 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -998,6 +998,12 @@ struct target_pollfd { #define TARGET_FIBMAP TARGET_IO(0x00,1) /* bmap access */ #define TARGET_FIGETBSZ TARGET_IO(0x00,2) /* get the block size used for bmap */ +/* Note that the ioctl numbers claim type "long" but the actual type + * used by the kernel is "int". + */ +#define TARGET_FS_IOC_GETFLAGS TARGET_IOR('f', 1, long) +#define TARGET_FS_IOC_SETFLAGS TARGET_IOW('f', 2, long) + #define TARGET_FS_IOC_FIEMAP TARGET_IOWR('f',11,struct fiemap) /* cdrom commands */ From 700fa58e4b9100d6bd77df06d2e5d1f457720c4d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 18 Jul 2016 11:47:55 +0100 Subject: [PATCH 05/26] linux-user: Use direct syscall for utimensat The linux utimensat syscall differs in semantics from the libc function because the syscall combines the features of utimensat() and futimens(). Rather than trying to split these apart in order to call the two libc functions which then call the same underlying syscall, just always directly make the host syscall. This fixes bugs in some of the corner cases which should return errors from the syscall but which we were incorrectly directing to futimens(). This doesn't reduce the set of hosts that our syscall implementation will work on, because if the direct syscall fails ENOSYS then the libc functions would also fail ENOSYS. (The system call has been in the kernel since 2.6.22 anyway.) Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 51f558d47d..21ae996dd1 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -520,16 +520,7 @@ static int sys_getcwd1(char *buf, size_t size) } #ifdef TARGET_NR_utimensat -#ifdef CONFIG_UTIMENSAT -static int sys_utimensat(int dirfd, const char *pathname, - const struct timespec times[2], int flags) -{ - if (pathname == NULL) - return futimens(dirfd, times); - else - return utimensat(dirfd, pathname, times, flags); -} -#elif defined(__NR_utimensat) +#if defined(__NR_utimensat) #define __NR_sys_utimensat __NR_utimensat _syscall4(int,sys_utimensat,int,dirfd,const char *,pathname, const struct timespec *,tsp,int,flags) From 2ba7fae3bd688f5bb6cb08defc731d77e6bd943c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 18 Jul 2016 15:35:59 +0100 Subject: [PATCH 06/26] linux-user: Check for bad event numbers in epoll_wait The kernel checks that the maxevents parameter to epoll_wait is non-negative and not larger than EP_MAX_EVENTS. Add this check to our implementation, so that: * we fail these cases EINVAL rather than EFAULT * we don't pass negative or overflowing values to the lock_user() size calculation Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 5 +++++ linux-user/syscall_defs.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 21ae996dd1..eecccbb25c 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -11501,6 +11501,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, int maxevents = arg3; int timeout = arg4; + if (maxevents <= 0 || maxevents > TARGET_EP_MAX_EVENTS) { + ret = -TARGET_EINVAL; + break; + } + target_ep = lock_user(VERIFY_WRITE, arg2, maxevents * sizeof(struct target_epoll_event), 1); if (!target_ep) { diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index c0e5cb0010..5c19c5ca19 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2585,6 +2585,9 @@ struct target_epoll_event { abi_uint events; target_epoll_data_t data; } TARGET_EPOLL_PACKED; + +#define TARGET_EP_MAX_EVENTS (INT_MAX / sizeof(struct target_epoll_event)) + #endif struct target_rlimit64 { uint64_t rlim_cur; From ce9c139d93db03e464341385976606b7568b768f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 18 Jul 2016 16:30:36 +0100 Subject: [PATCH 07/26] linux-user: Range check the nfds argument to ppoll syscall Do an initial range check on the ppoll syscall's nfds argument, to avoid possible overflow in the calculation of the lock_user() size argument. The host kernel will later apply the rather lower limit based on RLIMIT_NOFILE as appropriate. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index eecccbb25c..7a50a57d4b 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -9661,6 +9661,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, pfd = NULL; target_pfd = NULL; if (nfds) { + if (nfds > (INT_MAX / sizeof(struct target_pollfd))) { + ret = -TARGET_EINVAL; + break; + } + target_pfd = lock_user(VERIFY_WRITE, arg1, sizeof(struct target_pollfd) * nfds, 1); if (!target_pfd) { From 0cb581d6bdc5aa808ae1a9789d02657fe531cb39 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 18 Jul 2016 18:12:24 +0100 Subject: [PATCH 08/26] linux-user: report signals being taken in strace output Native strace reports when the process being traced takes a signal: --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} --- Report something similar when QEMU is doing its internal strace of the guest process and is about to deliver it a signal. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/qemu.h | 10 +++++ linux-user/signal.c | 4 ++ linux-user/strace.c | 106 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 815447f5fc..61808f6f35 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -362,6 +362,16 @@ void print_syscall(int num, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6); void print_syscall_ret(int num, abi_long arg1); +/** + * print_taken_signal: + * @target_signum: target signal being taken + * @tinfo: target_siginfo_t which will be passed to the guest for the signal + * + * Print strace output indicating that this signal is being taken by the guest, + * in a format similar to: + * --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} --- + */ +void print_taken_signal(int target_signum, const target_siginfo_t *tinfo); extern int do_strace; /* signal.c */ diff --git a/linux-user/signal.c b/linux-user/signal.c index d3ac0e2565..3337f1e563 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -5849,6 +5849,10 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig, handler = sa->_sa_handler; } + if (do_strace) { + print_taken_signal(sig, &k->info); + } + if (handler == TARGET_SIG_DFL) { /* default handler : ignore some signal. The other are job control or fatal */ if (sig == TARGET_SIGTSTP || sig == TARGET_SIGTTIN || sig == TARGET_SIGTTOU) { diff --git a/linux-user/strace.c b/linux-user/strace.c index cc10dc4703..1e5136098e 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -154,6 +154,100 @@ print_signal(abi_ulong arg, int last) gemu_log("%s%s", signal_name, get_comma(last)); } +static void print_si_code(int arg) +{ + const char *codename = NULL; + + switch (arg) { + case SI_USER: + codename = "SI_USER"; + break; + case SI_KERNEL: + codename = "SI_KERNEL"; + break; + case SI_QUEUE: + codename = "SI_QUEUE"; + break; + case SI_TIMER: + codename = "SI_TIMER"; + break; + case SI_MESGQ: + codename = "SI_MESGQ"; + break; + case SI_ASYNCIO: + codename = "SI_ASYNCIO"; + break; + case SI_SIGIO: + codename = "SI_SIGIO"; + break; + case SI_TKILL: + codename = "SI_TKILL"; + break; + default: + gemu_log("%d", arg); + return; + } + gemu_log("%s", codename); +} + +static void print_siginfo(const target_siginfo_t *tinfo) +{ + /* Print a target_siginfo_t in the format desired for printing + * signals being taken. We assume the target_siginfo_t is in the + * internal form where the top 16 bits of si_code indicate which + * part of the union is valid, rather than in the guest-visible + * form where the bottom 16 bits are sign-extended into the top 16. + */ + int si_type = extract32(tinfo->si_code, 16, 16); + int si_code = sextract32(tinfo->si_code, 0, 16); + + gemu_log("{si_signo="); + print_signal(tinfo->si_signo, 1); + gemu_log(", si_code="); + print_si_code(si_code); + + switch (si_type) { + case QEMU_SI_KILL: + gemu_log(", si_pid = %u, si_uid = %u", + (unsigned int)tinfo->_sifields._kill._pid, + (unsigned int)tinfo->_sifields._kill._uid); + break; + case QEMU_SI_TIMER: + gemu_log(", si_timer1 = %u, si_timer2 = %u", + tinfo->_sifields._timer._timer1, + tinfo->_sifields._timer._timer2); + break; + case QEMU_SI_POLL: + gemu_log(", si_band = %d, si_fd = %d", + tinfo->_sifields._sigpoll._band, + tinfo->_sifields._sigpoll._fd); + break; + case QEMU_SI_FAULT: + gemu_log(", si_addr = "); + print_pointer(tinfo->_sifields._sigfault._addr, 1); + break; + case QEMU_SI_CHLD: + gemu_log(", si_pid = %u, si_uid = %u, si_status = %d" + ", si_utime=" TARGET_ABI_FMT_ld + ", si_stime=" TARGET_ABI_FMT_ld, + (unsigned int)(tinfo->_sifields._sigchld._pid), + (unsigned int)(tinfo->_sifields._sigchld._uid), + tinfo->_sifields._sigchld._status, + tinfo->_sifields._sigchld._utime, + tinfo->_sifields._sigchld._stime); + break; + case QEMU_SI_RT: + gemu_log(", si_pid = %u, si_uid = %u, si_sigval = " TARGET_ABI_FMT_ld, + (unsigned int)tinfo->_sifields._rt._pid, + (unsigned int)tinfo->_sifields._rt._uid, + tinfo->_sifields._rt._sigval.sival_ptr); + break; + default: + g_assert_not_reached(); + } + gemu_log("}"); +} + static void print_sockaddr(abi_ulong addr, abi_long addrlen) { @@ -2190,3 +2284,15 @@ print_syscall_ret(int num, abi_long ret) break; } } + +void print_taken_signal(int target_signum, const target_siginfo_t *tinfo) +{ + /* Print the strace output for a signal being taken: + * --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} --- + */ + gemu_log("--- "); + print_signal(target_signum, 1); + gemu_log(" "); + print_siginfo(tinfo); + gemu_log(" ---\n"); +} From 68754b442b756e8cb5f143b00937fb7330a51a81 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Jul 2016 13:02:12 +0100 Subject: [PATCH 09/26] linux-user: Pass missing MAP_ANONYMOUS to target_mmap() call A target_mmap() call in load_elf_binary() was missing the MAP_ANONYMOUS flag. (Spotted by Coverity, because target_mmap() will try to use -1 as the filedescriptor in this case.) This has never been noticed because the code in question is for handling ancient SVr4 iBCS2 binaries. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/elfload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 29455e4e47..e9a3882bef 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2233,7 +2233,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) we do not have the power to recompile these, we emulate the SVr4 behavior. Sigh. */ target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE, -1, 0); + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); } } From 3211215e741f6e4824ddfc4919428e8d1b82a3c2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Jul 2016 13:02:13 +0100 Subject: [PATCH 10/26] linux-user: Check lock_user() return value for NULL lock_user() can return NULL, which typically means the syscall should fail with EFAULT. Add checks in various places where Coverity spotted that we were missing them. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 7a50a57d4b..efcc17a3b0 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5008,6 +5008,11 @@ static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, host_data = (char*)host_dm + host_dm->data_start; argptr = lock_user(VERIFY_READ, guest_data, guest_data_size, 1); + if (!argptr) { + ret = -TARGET_EFAULT; + goto out; + } + switch (ie->host_cmd) { case DM_REMOVE_ALL: case DM_LIST_DEVICES: @@ -11271,6 +11276,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_mq_unlink: p = lock_user_string(arg1 - 1); + if (!p) { + ret = -TARGET_EFAULT; + break; + } ret = get_errno(mq_unlink(p)); unlock_user (p, arg1, 0); break; From f9757b1d9649cb739ecf544c7137c0885281f6e8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Jul 2016 13:02:14 +0100 Subject: [PATCH 11/26] linux-user: Fix incorrect use of host errno in do_ioctl_dm() do_ioctl_dm() should return target errno values, not host ones; correct an accidental use of a host errno in an error path. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index efcc17a3b0..e28690713a 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5001,7 +5001,7 @@ static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, guest_data = arg + host_dm->data_start; if ((guest_data - arg) < 0) { - ret = -EINVAL; + ret = -TARGET_EINVAL; goto out; } guest_data_size = host_dm->data_size - host_dm->data_start; From e5a869ed569a97fa676e9827952629086ec41f4e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Jul 2016 13:02:15 +0100 Subject: [PATCH 12/26] linux-user: Fix error handling in flatload.c target_pread() The flatload.c target_pread() function is supposed to return 0 on success or negative host errnos; however it wasn't checking lock_user() for failure or returning the errno from the pread() call. Fix these problems (the first of which is noted by Coverity). Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/flatload.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/linux-user/flatload.c b/linux-user/flatload.c index 42d1079a24..a35a560904 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -95,7 +95,13 @@ static int target_pread(int fd, abi_ulong ptr, abi_ulong len, int ret; buf = lock_user(VERIFY_WRITE, ptr, len, 0); + if (!buf) { + return -EFAULT; + } ret = pread(fd, buf, len, offset); + if (ret < 0) { + ret = -errno; + } unlock_user(buf, ptr, len); return ret; } From 772034b63e9c0caf6c92e31413f2d8df2ee69c88 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Jul 2016 13:02:17 +0100 Subject: [PATCH 13/26] linux-user: Check dump_write() return in elf_core_dump() One of the calls to dump_write() in elf_core_dump() was missing a check for failure (spotted by Coverity). Add the check to bring it into line with the other calls from this function. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/elfload.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index e9a3882bef..0d07b85cb8 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -3050,7 +3050,9 @@ static int elf_core_dump(int signr, const CPUArchState *env) phdr.p_align = ELF_EXEC_PAGESIZE; bswap_phdr(&phdr, 1); - dump_write(fd, &phdr, sizeof (phdr)); + if (dump_write(fd, &phdr, sizeof(phdr)) != 0) { + goto out; + } } /* From 0ef9ea290ed9319cb788ea40be06dd18b32ba05a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Jul 2016 13:02:18 +0100 Subject: [PATCH 14/26] linux-user: Use glib malloc functions in load_symbols() Switch to using the glib malloc functions in load_symbols(); this deals with a Coverity complaint about possible integer overflow calculating the allocation size with 'nsyms * sizeof(*syms)'. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/elfload.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0d07b85cb8..3d751f8523 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2111,19 +2111,19 @@ static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias) found: /* Now know where the strtab and symtab are. Snarf them. */ - s = malloc(sizeof(*s)); + s = g_try_new(struct syminfo, 1); if (!s) { goto give_up; } i = shdr[str_idx].sh_size; - s->disas_strtab = strings = malloc(i); + s->disas_strtab = strings = g_try_malloc(i); if (!strings || pread(fd, strings, i, shdr[str_idx].sh_offset) != i) { goto give_up; } i = shdr[sym_idx].sh_size; - syms = malloc(i); + syms = g_try_malloc(i); if (!syms || pread(fd, syms, i, shdr[sym_idx].sh_offset) != i) { goto give_up; } @@ -2157,7 +2157,7 @@ static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias) that we threw away. Whether or not this has any effect on the memory allocation depends on the malloc implementation and how many symbols we managed to discard. */ - new_syms = realloc(syms, nsyms * sizeof(*syms)); + new_syms = g_try_renew(struct elf_sym, syms, nsyms); if (new_syms == NULL) { goto give_up; } @@ -2178,9 +2178,9 @@ static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias) return; give_up: - free(s); - free(strings); - free(syms); + g_free(s); + g_free(strings); + g_free(syms); } int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) From ee8e76141b4dd00f8e97fda274876a17f9a46bbe Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 11 Jul 2016 16:48:11 +0100 Subject: [PATCH 15/26] linux-user: Use correct target SHMLBA in shmat() The shmat() handling needs to do target-specific handling of the attach address for shmat(): * if the SHM_RND flag is passed, the address is rounded down to a SHMLBA boundary * if SHM_RND is not passed, then the call is failed EINVAL if the address is not a multiple of SHMLBA Since SHMLBA is target-specific, we need to do this checking and rounding in QEMU and can't leave it up to the host syscall. Allow targets to define TARGET_FORCE_SHMLBA and provide a target_shmlba() function if appropriate, and update do_shmat() to honour them. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/arm/target_syscall.h | 7 +++++ linux-user/mips/target_syscall.h | 7 +++++ linux-user/mips64/target_syscall.h | 7 +++++ linux-user/sh4/target_syscall.h | 7 +++++ linux-user/sparc/target_syscall.h | 16 +++++++++++ linux-user/syscall.c | 45 ++++++++++++++++++++++++++---- 6 files changed, 83 insertions(+), 6 deletions(-) diff --git a/linux-user/arm/target_syscall.h b/linux-user/arm/target_syscall.h index cd021ff598..0879b4d4a8 100644 --- a/linux-user/arm/target_syscall.h +++ b/linux-user/arm/target_syscall.h @@ -33,4 +33,11 @@ struct target_pt_regs { #define TARGET_MLOCKALL_MCL_CURRENT 1 #define TARGET_MLOCKALL_MCL_FUTURE 2 +#define TARGET_FORCE_SHMLBA + +static inline abi_ulong target_shmlba(CPUARMState *env) +{ + return 4 * 4096; +} + #endif /* ARM_TARGET_SYSCALL_H */ diff --git a/linux-user/mips/target_syscall.h b/linux-user/mips/target_syscall.h index 2b4f390729..6c666dcb73 100644 --- a/linux-user/mips/target_syscall.h +++ b/linux-user/mips/target_syscall.h @@ -230,4 +230,11 @@ struct target_pt_regs { #define TARGET_MLOCKALL_MCL_CURRENT 1 #define TARGET_MLOCKALL_MCL_FUTURE 2 +#define TARGET_FORCE_SHMLBA + +static inline abi_ulong target_shmlba(CPUMIPSState *env) +{ + return 0x40000; +} + #endif /* MIPS_TARGET_SYSCALL_H */ diff --git a/linux-user/mips64/target_syscall.h b/linux-user/mips64/target_syscall.h index 8da9c1f9cc..a9c17f7edf 100644 --- a/linux-user/mips64/target_syscall.h +++ b/linux-user/mips64/target_syscall.h @@ -227,4 +227,11 @@ struct target_pt_regs { #define TARGET_MLOCKALL_MCL_CURRENT 1 #define TARGET_MLOCKALL_MCL_FUTURE 2 +#define TARGET_FORCE_SHMLBA + +static inline abi_ulong target_shmlba(CPUMIPSState *env) +{ + return 0x40000; +} + #endif /* MIPS64_TARGET_SYSCALL_H */ diff --git a/linux-user/sh4/target_syscall.h b/linux-user/sh4/target_syscall.h index 78d5557124..2b5f75be13 100644 --- a/linux-user/sh4/target_syscall.h +++ b/linux-user/sh4/target_syscall.h @@ -19,4 +19,11 @@ struct target_pt_regs { #define TARGET_MLOCKALL_MCL_CURRENT 1 #define TARGET_MLOCKALL_MCL_FUTURE 2 +#define TARGET_FORCE_SHMLBA + +static inline abi_ulong target_shmlba(CPUSH4State *env) +{ + return 0x4000; +} + #endif /* SH4_TARGET_SYSCALL_H */ diff --git a/linux-user/sparc/target_syscall.h b/linux-user/sparc/target_syscall.h index 326f674b4e..f97aa6b075 100644 --- a/linux-user/sparc/target_syscall.h +++ b/linux-user/sparc/target_syscall.h @@ -22,4 +22,20 @@ struct target_pt_regs { #define TARGET_MLOCKALL_MCL_CURRENT 0x2000 #define TARGET_MLOCKALL_MCL_FUTURE 0x4000 +/* For SPARC SHMLBA is determined at runtime in the kernel, and + * libc has to runtime-detect it using the hwcaps (see glibc + * sysdeps/unix/sysv/linux/sparc/getshmlba; we follow the same + * logic here, though we know we're not the sparc v9 64-bit case). + */ +#define TARGET_FORCE_SHMLBA + +static inline abi_ulong target_shmlba(CPUSPARCState *env) +{ + if (!(env->def->features & CPU_FEATURE_FLUSH)) { + return 64 * 1024; + } else { + return 256 * 1024; + } +} + #endif /* SPARC_TARGET_SYSCALL_H */ diff --git a/linux-user/syscall.c b/linux-user/syscall.c index e28690713a..85699f9f31 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -4575,12 +4575,34 @@ static inline abi_long do_shmctl(int shmid, int cmd, abi_long buf) return ret; } -static inline abi_ulong do_shmat(int shmid, abi_ulong shmaddr, int shmflg) +#ifndef TARGET_FORCE_SHMLBA +/* For most architectures, SHMLBA is the same as the page size; + * some architectures have larger values, in which case they should + * define TARGET_FORCE_SHMLBA and provide a target_shmlba() function. + * This corresponds to the kernel arch code defining __ARCH_FORCE_SHMLBA + * and defining its own value for SHMLBA. + * + * The kernel also permits SHMLBA to be set by the architecture to a + * value larger than the page size without setting __ARCH_FORCE_SHMLBA; + * this means that addresses are rounded to the large size if + * SHM_RND is set but addresses not aligned to that size are not rejected + * as long as they are at least page-aligned. Since the only architecture + * which uses this is ia64 this code doesn't provide for that oddity. + */ +static inline abi_ulong target_shmlba(CPUArchState *cpu_env) +{ + return TARGET_PAGE_SIZE; +} +#endif + +static inline abi_ulong do_shmat(CPUArchState *cpu_env, + int shmid, abi_ulong shmaddr, int shmflg) { abi_long raddr; void *host_raddr; struct shmid_ds shm_info; int i,ret; + abi_ulong shmlba; /* find out the length of the shared memory segment */ ret = get_errno(shmctl(shmid, IPC_STAT, &shm_info)); @@ -4589,6 +4611,16 @@ static inline abi_ulong do_shmat(int shmid, abi_ulong shmaddr, int shmflg) return ret; } + shmlba = target_shmlba(cpu_env); + + if (shmaddr & (shmlba - 1)) { + if (shmflg & SHM_RND) { + shmaddr &= ~(shmlba - 1); + } else { + return -TARGET_EINVAL; + } + } + mmap_lock(); if (shmaddr) @@ -4647,7 +4679,8 @@ static inline abi_long do_shmdt(abi_ulong shmaddr) #ifdef TARGET_NR_ipc /* ??? This only works with linear mappings. */ /* do_ipc() must return target values and target errnos. */ -static abi_long do_ipc(unsigned int call, abi_long first, +static abi_long do_ipc(CPUArchState *cpu_env, + unsigned int call, abi_long first, abi_long second, abi_long third, abi_long ptr, abi_long fifth) { @@ -4716,7 +4749,7 @@ static abi_long do_ipc(unsigned int call, abi_long first, default: { abi_ulong raddr; - raddr = do_shmat(first, ptr, second); + raddr = do_shmat(cpu_env, first, ptr, second); if (is_error(raddr)) return get_errno(raddr); if (put_user_ual(raddr, third)) @@ -9304,8 +9337,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; #ifdef TARGET_NR_ipc case TARGET_NR_ipc: - ret = do_ipc(arg1, arg2, arg3, arg4, arg5, arg6); - break; + ret = do_ipc(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6); + break; #endif #ifdef TARGET_NR_semget case TARGET_NR_semget: @@ -9354,7 +9387,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_shmat case TARGET_NR_shmat: - ret = do_shmat(arg1, arg2, arg3); + ret = do_shmat(cpu_env, arg1, arg2, arg3); break; #endif #ifdef TARGET_NR_shmdt From c836112997e19966565cd6eb68e0836c8972720b Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Fri, 22 Jul 2016 17:18:05 +0200 Subject: [PATCH 16/26] linux-user: ppc64: set MSR_CM bit for BookE 2.06 MMU 64 bit user mode doesn't work for the e5500 core because the MSR_CM bit is not set which enables the 64 bit mode for this MMU model. Memory addresses are truncated to 32 bit, which results in "Invalid data memory access" error messages. Fix it by setting the MSR_CM bit for this MMU model. Signed-off-by: Michael Walle Reviewed-by: Alexander Graf Signed-off-by: Riku Voipio --- linux-user/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 3ad70f8a6e..2aeda8a10d 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -4615,10 +4615,11 @@ int main(int argc, char **argv, char **envp) int i; #if defined(TARGET_PPC64) + int flag = (env->insns_flags2 & PPC2_BOOKE206) ? MSR_CM : MSR_SF; #if defined(TARGET_ABI32) - env->msr &= ~((target_ulong)1 << MSR_SF); + env->msr &= ~((target_ulong)1 << flag); #else - env->msr |= (target_ulong)1 << MSR_SF; + env->msr |= (target_ulong)1 << flag; #endif #endif env->nip = regs->nip; From 8bd3773cce1885c46e5cf09a72b9138ccba5fbf2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Jul 2016 16:44:45 +0100 Subject: [PATCH 17/26] linux-user: Recheck for pending synchronous signals too In process_pending_signals() we restart the scan of possible pending signals after calling handle_pending_signal() in case some other signal has been generated. This rescan should also include a check for a new synchronous signal since those are in fact the only kind of new signal that the signal frame setup process might produce. Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/signal.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index 3337f1e563..f2c9f8e474 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -5925,6 +5925,7 @@ void process_pending_signals(CPUArchState *cpu_env) sigfillset(&set); sigprocmask(SIG_SETMASK, &set, 0); + restart_scan: sig = ts->sync_signal.pending; if (sig) { /* Synchronous signals are forced, @@ -5952,8 +5953,10 @@ void process_pending_signals(CPUArchState *cpu_env) (!sigismember(blocked_set, target_to_host_signal_table[sig]))) { handle_pending_signal(cpu_env, sig, &ts->sigtab[sig - 1]); - /* Restart scan from the beginning */ - sig = 1; + /* Restart scan from the beginning, as handle_pending_signal + * might have resulted in a new synchronous signal (eg SIGSEGV). + */ + goto restart_scan; } } From 9d2803f720d5b71937c0f564bb2c16d8f5e18c8c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Jul 2016 16:44:46 +0100 Subject: [PATCH 18/26] linux-user: Pass si_type information to queue_signal() explicitly Instead of assuming in queue_signal() that all callers are passing a siginfo structure which uses the _sifields._sigfault part of the union (and thus a si_type of QEMU_SI_FAULT), make callers pass the si_type they require in as an argument. [RV adjusted to apply] Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/main.c | 124 +++++++++++++++++++++---------------------- linux-user/qemu.h | 3 +- linux-user/signal.c | 10 ++-- linux-user/syscall.c | 6 ++- 4 files changed, 71 insertions(+), 72 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 2aeda8a10d..aba58c78bc 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -339,7 +339,7 @@ void cpu_loop(CPUX86State *env) info.si_errno = 0; info.si_code = TARGET_SI_KERNEL; info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP0D_GPF: /* XXX: potential problem if ABI32 */ @@ -353,7 +353,7 @@ void cpu_loop(CPUX86State *env) info.si_errno = 0; info.si_code = TARGET_SI_KERNEL; info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } break; case EXCP0E_PAGE: @@ -364,7 +364,7 @@ void cpu_loop(CPUX86State *env) else info.si_code = TARGET_SEGV_ACCERR; info._sifields._sigfault._addr = env->cr[2]; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP00_DIVZ: #ifndef TARGET_X86_64 @@ -378,7 +378,7 @@ void cpu_loop(CPUX86State *env) info.si_errno = 0; info.si_code = TARGET_FPE_INTDIV; info._sifields._sigfault._addr = env->eip; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } break; case EXCP01_DB: @@ -398,7 +398,7 @@ void cpu_loop(CPUX86State *env) info.si_code = TARGET_SI_KERNEL; info._sifields._sigfault._addr = 0; } - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } break; case EXCP04_INTO: @@ -413,7 +413,7 @@ void cpu_loop(CPUX86State *env) info.si_errno = 0; info.si_code = TARGET_SI_KERNEL; info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } break; case EXCP06_ILLOP: @@ -421,7 +421,7 @@ void cpu_loop(CPUX86State *env) info.si_errno = 0; info.si_code = TARGET_ILL_ILLOPN; info._sifields._sigfault._addr = env->eip; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ @@ -436,7 +436,7 @@ void cpu_loop(CPUX86State *env) info.si_signo = sig; info.si_errno = 0; info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } } break; @@ -576,7 +576,7 @@ segv: /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; info._sifields._sigfault._addr = env->exception.vaddress; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } /* Handle a jump to the kernel code page. */ @@ -755,7 +755,7 @@ void cpu_loop(CPUARMState *env) info.si_errno = 0; info.si_code = TARGET_ILL_ILLOPN; info._sifields._sigfault._addr = env->regs[15]; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } else if (rc < 0) { /* FP exception */ int arm_fpe=0; @@ -786,7 +786,7 @@ void cpu_loop(CPUARMState *env) if (arm_fpe & BIT_IOC) info.si_code = TARGET_FPE_FLTINV; info._sifields._sigfault._addr = env->regs[15]; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } else { env->regs[15] += 4; } @@ -907,7 +907,7 @@ void cpu_loop(CPUARMState *env) /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; info._sifields._sigfault._addr = addr; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } break; case EXCP_DEBUG: @@ -921,7 +921,7 @@ void cpu_loop(CPUARMState *env) info.si_signo = sig; info.si_errno = 0; info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } } break; @@ -1099,7 +1099,7 @@ void cpu_loop(CPUARMState *env) info.si_errno = 0; info.si_code = TARGET_ILL_ILLOPN; info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_STREX: if (!do_strex_a64(env)) { @@ -1113,7 +1113,7 @@ void cpu_loop(CPUARMState *env) /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; info._sifields._sigfault._addr = env->exception.vaddress; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_DEBUG: case EXCP_BKPT: @@ -1122,7 +1122,7 @@ void cpu_loop(CPUARMState *env) info.si_signo = sig; info.si_errno = 0; info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } break; case EXCP_SEMIHOST: @@ -1202,7 +1202,7 @@ void cpu_loop(CPUUniCore32State *env) /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; info._sifields._sigfault._addr = env->cp0.c4_faultaddr; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ @@ -1216,7 +1216,7 @@ void cpu_loop(CPUUniCore32State *env) info.si_signo = sig; info.si_errno = 0; info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } } break; @@ -1431,7 +1431,7 @@ void cpu_loop (CPUSPARCState *env) /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; info._sifields._sigfault._addr = env->mmuregs[4]; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } break; #else @@ -1452,7 +1452,7 @@ void cpu_loop (CPUSPARCState *env) info._sifields._sigfault._addr = env->dmmuregs[4]; else info._sifields._sigfault._addr = cpu_tsptr(env)->tpc; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } break; #ifndef TARGET_ABI32 @@ -1475,7 +1475,7 @@ void cpu_loop (CPUSPARCState *env) info.si_errno = 0; info.si_code = TARGET_ILL_ILLOPC; info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } break; case EXCP_DEBUG: @@ -1488,7 +1488,7 @@ void cpu_loop (CPUSPARCState *env) info.si_signo = sig; info.si_errno = 0; info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } } break; @@ -1679,7 +1679,7 @@ void cpu_loop(CPUPPCState *env) break; } info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case POWERPC_EXCP_ISI: /* Instruction storage exception */ /* XXX: check this */ @@ -1705,7 +1705,7 @@ void cpu_loop(CPUPPCState *env) break; } info._sifields._sigfault._addr = env->nip - 4; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case POWERPC_EXCP_EXTERNAL: /* External input */ cpu_abort(cs, "External interrupt while in user mode. " @@ -1717,7 +1717,7 @@ void cpu_loop(CPUPPCState *env) info.si_errno = 0; info.si_code = TARGET_BUS_ADRALN; info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case POWERPC_EXCP_PROGRAM: /* Program exception */ case POWERPC_EXCP_HV_EMU: /* HV emulation */ @@ -1808,14 +1808,14 @@ void cpu_loop(CPUPPCState *env) break; } info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ info.si_signo = TARGET_SIGILL; info.si_errno = 0; info.si_code = TARGET_ILL_COPROC; info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case POWERPC_EXCP_SYSCALL: /* System call exception */ cpu_abort(cs, "Syscall exception while in user mode. " @@ -1826,7 +1826,7 @@ void cpu_loop(CPUPPCState *env) info.si_errno = 0; info.si_code = TARGET_ILL_COPROC; info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case POWERPC_EXCP_DECR: /* Decrementer exception */ cpu_abort(cs, "Decrementer interrupt while in user mode. " @@ -1853,7 +1853,7 @@ void cpu_loop(CPUPPCState *env) info.si_errno = 0; info.si_code = TARGET_ILL_COPROC; info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case POWERPC_EXCP_EFPDI: /* Embedded floating-point data IRQ */ cpu_abort(cs, "Embedded floating-point data IRQ not handled\n"); @@ -1916,7 +1916,7 @@ void cpu_loop(CPUPPCState *env) info.si_errno = 0; info.si_code = TARGET_ILL_COPROC; info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case POWERPC_EXCP_PIT: /* Programmable interval timer IRQ */ cpu_abort(cs, "Programmable interval timer interrupt " @@ -2010,7 +2010,7 @@ void cpu_loop(CPUPPCState *env) info.si_errno = 0; info.si_code = TARGET_SEGV_MAPERR; info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } break; case EXCP_DEBUG: @@ -2022,7 +2022,7 @@ void cpu_loop(CPUPPCState *env) info.si_signo = sig; info.si_errno = 0; info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } } break; @@ -2456,13 +2456,13 @@ static int do_break(CPUMIPSState *env, target_siginfo_t *info, info->si_signo = TARGET_SIGFPE; info->si_errno = 0; info->si_code = (code == BRK_OVERFLOW) ? FPE_INTOVF : FPE_INTDIV; - queue_signal(env, info->si_signo, &*info); + queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); ret = 0; break; default: info->si_signo = TARGET_SIGTRAP; info->si_errno = 0; - queue_signal(env, info->si_signo, &*info); + queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); ret = 0; break; } @@ -2560,14 +2560,14 @@ done_syscall: /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; info._sifields._sigfault._addr = env->CP0_BadVAddr; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_CpU: case EXCP_RI: info.si_signo = TARGET_SIGILL; info.si_errno = 0; info.si_code = 0; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ @@ -2582,7 +2582,7 @@ done_syscall: info.si_signo = sig; info.si_errno = 0; info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } } break; @@ -2592,14 +2592,14 @@ done_syscall: info.si_errno = 0; info.si_code = TARGET_SEGV_MAPERR; info._sifields._sigfault._addr = env->active_tc.PC; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } break; case EXCP_DSPDIS: info.si_signo = TARGET_SIGILL; info.si_errno = 0; info.si_code = TARGET_ILL_ILLOPC; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; /* The code below was inspired by the MIPS Linux kernel trap * handling code in arch/mips/kernel/traps.c. @@ -2850,7 +2850,7 @@ void cpu_loop(CPUSH4State *env) info.si_signo = sig; info.si_errno = 0; info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } } break; @@ -2860,7 +2860,7 @@ void cpu_loop(CPUSH4State *env) info.si_errno = 0; info.si_code = TARGET_SEGV_MAPERR; info._sifields._sigfault._addr = env->tea; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; default: @@ -2892,7 +2892,7 @@ void cpu_loop(CPUCRISState *env) /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; info._sifields._sigfault._addr = env->pregs[PR_EDA]; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } break; case EXCP_INTERRUPT: @@ -2924,7 +2924,7 @@ void cpu_loop(CPUCRISState *env) info.si_signo = sig; info.si_errno = 0; info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } } break; @@ -2957,7 +2957,7 @@ void cpu_loop(CPUMBState *env) /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } break; case EXCP_INTERRUPT: @@ -3006,7 +3006,7 @@ void cpu_loop(CPUMBState *env) info.si_errno = 0; info.si_code = TARGET_FPE_FLTDIV; info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case ESR_EC_FPU: info.si_signo = TARGET_SIGFPE; @@ -3018,7 +3018,7 @@ void cpu_loop(CPUMBState *env) info.si_code = TARGET_FPE_FLTDIV; } info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; default: printf ("Unhandled hw-exception: 0x%x\n", @@ -3038,7 +3038,7 @@ void cpu_loop(CPUMBState *env) info.si_signo = sig; info.si_errno = 0; info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } } break; @@ -3092,7 +3092,7 @@ void cpu_loop(CPUM68KState *env) info.si_errno = 0; info.si_code = TARGET_ILL_ILLOPN; info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_TRAP0: { @@ -3126,7 +3126,7 @@ void cpu_loop(CPUM68KState *env) /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; info._sifields._sigfault._addr = env->mmu.ar; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } break; case EXCP_DEBUG: @@ -3139,7 +3139,7 @@ void cpu_loop(CPUM68KState *env) info.si_signo = sig; info.si_errno = 0; info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } } break; @@ -3195,7 +3195,7 @@ static void do_store_exclusive(CPUAlphaState *env, int reg, int quad) info.si_errno = 0; info.si_code = TARGET_SEGV_MAPERR; info._sifields._sigfault._addr = addr; - queue_signal(env, TARGET_SIGSEGV, &info); + queue_signal(env, TARGET_SIGSEGV, QEMU_SI_FAULT, &info); } void cpu_loop(CPUAlphaState *env) @@ -3237,7 +3237,7 @@ void cpu_loop(CPUAlphaState *env) info.si_code = (page_get_flags(env->trap_arg0) & PAGE_VALID ? TARGET_SEGV_ACCERR : TARGET_SEGV_MAPERR); info._sifields._sigfault._addr = env->trap_arg0; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_UNALIGN: env->lock_addr = -1; @@ -3245,7 +3245,7 @@ void cpu_loop(CPUAlphaState *env) info.si_errno = 0; info.si_code = TARGET_BUS_ADRALN; info._sifields._sigfault._addr = env->trap_arg0; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_OPCDEC: do_sigill: @@ -3254,7 +3254,7 @@ void cpu_loop(CPUAlphaState *env) info.si_errno = 0; info.si_code = TARGET_ILL_ILLOPC; info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_ARITH: env->lock_addr = -1; @@ -3262,7 +3262,7 @@ void cpu_loop(CPUAlphaState *env) info.si_errno = 0; info.si_code = TARGET_FPE_FLTINV; info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_FEN: /* No-op. Linux simply re-enables the FPU. */ @@ -3276,7 +3276,7 @@ void cpu_loop(CPUAlphaState *env) info.si_errno = 0; info.si_code = TARGET_TRAP_BRKPT; info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case 0x81: /* BUGCHK */ @@ -3284,7 +3284,7 @@ void cpu_loop(CPUAlphaState *env) info.si_errno = 0; info.si_code = 0; info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case 0x83: /* CALLSYS */ @@ -3356,7 +3356,7 @@ void cpu_loop(CPUAlphaState *env) } info.si_errno = 0; info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; default: goto do_sigill; @@ -3368,7 +3368,7 @@ void cpu_loop(CPUAlphaState *env) env->lock_addr = -1; info.si_errno = 0; info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } break; case EXCP_STL_C: @@ -3502,7 +3502,7 @@ void cpu_loop(CPUS390XState *env) info.si_errno = 0; info.si_code = n; info._sifields._sigfault._addr = addr; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; default: @@ -3526,7 +3526,7 @@ static void gen_sigill_reg(CPUTLGState *env) info.si_errno = 0; info.si_code = TARGET_ILL_PRVREG; info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } static void do_signal(CPUTLGState *env, int signo, int sigcode) @@ -3550,7 +3550,7 @@ static void do_signal(CPUTLGState *env, int signo, int sigcode) } info.si_code = sigcode; - queue_signal(env, info.si_signo, &info); + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } static void gen_sigsegv_maperr(CPUTLGState *env, target_ulong addr) diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 61808f6f35..da73a01106 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -377,7 +377,8 @@ extern int do_strace; /* signal.c */ void process_pending_signals(CPUArchState *cpu_env); void signal_init(void); -int queue_signal(CPUArchState *env, int sig, target_siginfo_t *info); +int queue_signal(CPUArchState *env, int sig, int si_type, + target_siginfo_t *info); void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info); void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo); int target_to_host_signal(int sig); diff --git a/linux-user/signal.c b/linux-user/signal.c index f2c9f8e474..93a9293641 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -569,19 +569,15 @@ static void QEMU_NORETURN force_sig(int target_sig) /* queue a signal so that it will be send to the virtual CPU as soon as possible */ -int queue_signal(CPUArchState *env, int sig, target_siginfo_t *info) +int queue_signal(CPUArchState *env, int sig, int si_type, + target_siginfo_t *info) { CPUState *cpu = ENV_GET_CPU(env); TaskState *ts = cpu->opaque; trace_user_queue_signal(env, sig); - /* Currently all callers define siginfo structures which - * use the _sifields._sigfault union member, so we can - * set the type here. If that changes we should push this - * out so the si_type is passed in by callers. - */ - info->si_code = deposit32(info->si_code, 16, 16, QEMU_SI_FAULT); + info->si_code = deposit32(info->si_code, 16, 16, si_type); ts->sync_signal.info = *info; ts->sync_signal.pending = sig; diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 85699f9f31..27ad6a2a6c 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -10577,7 +10577,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, info.si_code = si_code; info._sifields._sigfault._addr = ((CPUArchState *)cpu_env)->pc; - queue_signal((CPUArchState *)cpu_env, info.si_signo, &info); + queue_signal((CPUArchState *)cpu_env, info.si_signo, + QEMU_SI_FAULT, &info); } } break; @@ -11665,7 +11666,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, info.si_errno = 0; info.si_code = TARGET_SEGV_MAPERR; info._sifields._sigfault._addr = arg6; - queue_signal((CPUArchState *)cpu_env, info.si_signo, &info); + queue_signal((CPUArchState *)cpu_env, info.si_signo, + QEMU_SI_FAULT, &info); ret = 0xdeadbeef; } From 09391669975a0e2882c181982d4ddee35a0080bb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Jul 2016 16:44:47 +0100 Subject: [PATCH 19/26] linux-user: SIGSEGV on signal entry need not be fatal A failed write to memory trying to set up the signal frame should trigger a SIGSEGV, but this need not be fatal: the guest has a chance to catch it. Implement this via a force_sigsegv() function with the same behaviour as the kernel function of that name: make sure that we don't try to re-take a failed SIGSEGV, and force a synchronous signal. Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/signal.c | 87 +++++++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index 93a9293641..892b527a37 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -512,6 +512,33 @@ void signal_init(void) } } +#if !((defined(TARGET_ARM) && !defined(TARGET_AARCH64)) || \ + defined(TARGET_X86_64) || defined(TARGET_UNICORE32)) + +/* Force a SIGSEGV if we couldn't write to memory trying to set + * up the signal frame. oldsig is the signal we were trying to handle + * at the point of failure. + */ +static void force_sigsegv(int oldsig) +{ + CPUState *cpu = thread_cpu; + CPUArchState *env = cpu->env_ptr; + target_siginfo_t info; + + if (oldsig == SIGSEGV) { + /* Make sure we don't try to deliver the signal again; this will + * end up with handle_pending_signal() calling force_sig(). + */ + sigact_table[oldsig - 1]._sa_handler = TARGET_SIG_DFL; + } + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SI_KERNEL; + info._sifields._kill._pid = 0; + info._sifields._kill._uid = 0; + queue_signal(env, info.si_signo, QEMU_SI_KILL, &info); +} +#endif /* abort execution with signal */ static void QEMU_NORETURN force_sig(int target_sig) @@ -1011,10 +1038,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, return; give_sigsegv: - if (sig == TARGET_SIGSEGV) { - ka->_sa_handler = TARGET_SIG_DFL; - } - force_sig(TARGET_SIGSEGV /* , current */); + force_sigsegv(sig); } /* compare linux/arch/i386/kernel/signal.c:setup_rt_frame() */ @@ -1084,10 +1108,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, return; give_sigsegv: - if (sig == TARGET_SIGSEGV) { - ka->_sa_handler = TARGET_SIG_DFL; - } - force_sig(TARGET_SIGSEGV /* , current */); + force_sigsegv(sig); } static int @@ -1416,7 +1437,7 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, give_sigsegv: unlock_user_struct(frame, frame_addr, 1); - force_sig(TARGET_SIGSEGV); + force_sigsegv(usig); } static void setup_rt_frame(int sig, struct target_sigaction *ka, @@ -2441,7 +2462,7 @@ sigill_and_return: #endif sigsegv: unlock_user(sf, sf_addr, sizeof(struct target_signal_frame)); - force_sig(TARGET_SIGSEGV); + force_sigsegv(sig); } static void setup_rt_frame(int sig, struct target_sigaction *ka, @@ -3033,7 +3054,7 @@ static void setup_frame(int sig, struct target_sigaction * ka, return; give_sigsegv: - force_sig(TARGET_SIGSEGV/*, current*/); + force_sigsegv(sig); } long do_sigreturn(CPUMIPSState *regs) @@ -3142,7 +3163,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, give_sigsegv: unlock_user_struct(frame, frame_addr, 1); - force_sig(TARGET_SIGSEGV/*, current*/); + force_sigsegv(sig); } long do_rt_sigreturn(CPUMIPSState *env) @@ -3345,7 +3366,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, give_sigsegv: unlock_user_struct(frame, frame_addr, 1); - force_sig(TARGET_SIGSEGV); + force_sigsegv(sig); } static void setup_rt_frame(int sig, struct target_sigaction *ka, @@ -3405,7 +3426,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, give_sigsegv: unlock_user_struct(frame, frame_addr, 1); - force_sig(TARGET_SIGSEGV); + force_sigsegv(sig); } long do_sigreturn(CPUSH4State *regs) @@ -3652,7 +3673,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, unlock_user_struct(frame, frame_addr, 1); return; badframe: - force_sig(TARGET_SIGSEGV); + force_sigsegv(sig); } static void setup_rt_frame(int sig, struct target_sigaction *ka, @@ -3822,7 +3843,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, unlock_user_struct(frame, frame_addr, 1); return; badframe: - force_sig(TARGET_SIGSEGV); + force_sigsegv(sig); } static void setup_rt_frame(int sig, struct target_sigaction *ka, @@ -4061,10 +4082,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, give_sigsegv: unlock_user_struct(frame, frame_addr, 1); - if (sig == TARGET_SIGSEGV) { - ka->_sa_handler = TARGET_SIG_DFL; - } - force_sig(TARGET_SIGSEGV); + force_sigsegv(sig); } long do_sigreturn(CPUOpenRISCState *env) @@ -4245,7 +4263,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, return; give_sigsegv: - force_sig(TARGET_SIGSEGV); + force_sigsegv(sig); } static void setup_rt_frame(int sig, struct target_sigaction *ka, @@ -4300,7 +4318,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, return; give_sigsegv: - force_sig(TARGET_SIGSEGV); + force_sigsegv(sig); } static int @@ -4811,7 +4829,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, sigsegv: unlock_user_struct(frame, frame_addr, 1); - force_sig(TARGET_SIGSEGV); + force_sigsegv(sig); } static void setup_rt_frame(int sig, struct target_sigaction *ka, @@ -4906,7 +4924,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, sigsegv: unlock_user_struct(rt_sf, rt_sf_addr, 1); - force_sig(TARGET_SIGSEGV); + force_sigsegv(sig); } @@ -5155,7 +5173,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, return; give_sigsegv: - force_sig(TARGET_SIGSEGV); + force_sigsegv(sig); } static inline int target_rt_setup_ucontext(struct target_ucontext *uc, @@ -5294,7 +5312,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, give_sigsegv: unlock_user_struct(frame, frame_addr, 1); - force_sig(TARGET_SIGSEGV); + force_sigsegv(sig); } long do_sigreturn(CPUM68KState *env) @@ -5501,10 +5519,8 @@ static void setup_frame(int sig, struct target_sigaction *ka, if (err) { give_sigsegv: - if (sig == TARGET_SIGSEGV) { - ka->_sa_handler = TARGET_SIG_DFL; - } - force_sig(TARGET_SIGSEGV); + force_sigsegv(sig); + return; } env->ir[IR_RA] = r26; @@ -5558,10 +5574,8 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, if (err) { give_sigsegv: - if (sig == TARGET_SIGSEGV) { - ka->_sa_handler = TARGET_SIG_DFL; - } - force_sig(TARGET_SIGSEGV); + force_sigsegv(sig); + return; } env->ir[IR_RA] = r26; @@ -5758,10 +5772,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, return; give_sigsegv: - if (sig == TARGET_SIGSEGV) { - ka->_sa_handler = TARGET_SIG_DFL; - } - force_sig(TARGET_SIGSEGV /* , current */); + force_sigsegv(sig); } long do_rt_sigreturn(CPUTLGState *env) From 28298c912e2c379f2b7767b351beeb77f001f27f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Jul 2016 16:44:48 +0100 Subject: [PATCH 20/26] linux-user: ARM: Give SIGSEGV if signal frame setup fails The 32-bit ARM signal frame setup code was just bailing out on error returns from lock_user_struct calls, without generating the SIGSEGV that should happen here. Wire up error return codes to call force_sigsegv(). Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/signal.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index 892b527a37..43836960c1 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -512,8 +512,7 @@ void signal_init(void) } } -#if !((defined(TARGET_ARM) && !defined(TARGET_AARCH64)) || \ - defined(TARGET_X86_64) || defined(TARGET_UNICORE32)) +#if !(defined(TARGET_X86_64) || defined(TARGET_UNICORE32)) /* Force a SIGSEGV if we couldn't write to memory trying to set * up the signal frame. oldsig is the signal we were trying to handle @@ -1789,7 +1788,7 @@ static void setup_frame_v1(int usig, struct target_sigaction *ka, trace_user_setup_frame(regs, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - return; + goto sigsegv; } setup_sigcontext(&frame->sc, regs, set->sig[0]); @@ -1802,6 +1801,9 @@ static void setup_frame_v1(int usig, struct target_sigaction *ka, frame_addr + offsetof(struct sigframe_v1, retcode)); unlock_user_struct(frame, frame_addr, 1); + return; +sigsegv: + force_sigsegv(usig); } static void setup_frame_v2(int usig, struct target_sigaction *ka, @@ -1812,7 +1814,7 @@ static void setup_frame_v2(int usig, struct target_sigaction *ka, trace_user_setup_frame(regs, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - return; + goto sigsegv; } setup_sigframe_v2(&frame->uc, set, regs); @@ -1821,6 +1823,9 @@ static void setup_frame_v2(int usig, struct target_sigaction *ka, frame_addr + offsetof(struct sigframe_v2, retcode)); unlock_user_struct(frame, frame_addr, 1); + return; +sigsegv: + force_sigsegv(usig); } static void setup_frame(int usig, struct target_sigaction *ka, @@ -1846,7 +1851,7 @@ static void setup_rt_frame_v1(int usig, struct target_sigaction *ka, trace_user_setup_rt_frame(env, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - return /* 1 */; + goto sigsegv; } info_addr = frame_addr + offsetof(struct rt_sigframe_v1, info); @@ -1876,6 +1881,9 @@ static void setup_rt_frame_v1(int usig, struct target_sigaction *ka, env->regs[2] = uc_addr; unlock_user_struct(frame, frame_addr, 1); + return; +sigsegv: + force_sigsegv(usig); } static void setup_rt_frame_v2(int usig, struct target_sigaction *ka, @@ -1888,7 +1896,7 @@ static void setup_rt_frame_v2(int usig, struct target_sigaction *ka, trace_user_setup_rt_frame(env, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - return /* 1 */; + goto sigsegv; } info_addr = frame_addr + offsetof(struct rt_sigframe_v2, info); @@ -1904,6 +1912,9 @@ static void setup_rt_frame_v2(int usig, struct target_sigaction *ka, env->regs[2] = uc_addr; unlock_user_struct(frame, frame_addr, 1); + return; +sigsegv: + force_sigsegv(usig); } static void setup_rt_frame(int usig, struct target_sigaction *ka, From c599d4d6d6e9bfdb64e54c33a22cb26e3496b96d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Jul 2016 16:44:49 +0100 Subject: [PATCH 21/26] linux-user: SIGSEGV from sigreturn need not be fatal If the sigreturn syscall fails to read memory then this causes a SIGSEGV, but this is not necessarily a fatal signal -- the guest process can catch it. We don't implement this correctly because the behaviour of QEMU's force_sig() function has drifted away from the kernel function of the same name -- ours now does "always do a guest core dump and abort execution", whereas the kernel version simply forces the guest to take a signal, which may or may not eventually cause a core dump. Rename our force_sig() to dump_core_and_abort(), and provide a force_sig() which acts more like the kernel version as the sigreturn implementations expect it to. Since force_sig() now returns, we must update all the callsites to return -TARGET_QEMU_ESIGRETURN so that the main loop doesn't change the guest registers before the signal handler is invoked. Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/signal.c | 81 ++++++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index 43836960c1..60fda18117 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -512,6 +512,27 @@ void signal_init(void) } } +#if !defined(TARGET_OPENRISC) && !defined(TARGET_UNICORE32) && \ + !defined(TARGET_X86_64) +/* Force a synchronously taken signal. The kernel force_sig() function + * also forces the signal to "not blocked, not ignored", but for QEMU + * that work is done in process_pending_signals(). + */ +static void force_sig(int sig) +{ + CPUState *cpu = thread_cpu; + CPUArchState *env = cpu->env_ptr; + target_siginfo_t info; + + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_SI_KERNEL; + info._sifields._kill._pid = 0; + info._sifields._kill._uid = 0; + queue_signal(env, info.si_signo, QEMU_SI_KILL, &info); +} +#endif + #if !(defined(TARGET_X86_64) || defined(TARGET_UNICORE32)) /* Force a SIGSEGV if we couldn't write to memory trying to set @@ -526,7 +547,7 @@ static void force_sigsegv(int oldsig) if (oldsig == SIGSEGV) { /* Make sure we don't try to deliver the signal again; this will - * end up with handle_pending_signal() calling force_sig(). + * end up with handle_pending_signal() calling dump_core_and_abort(). */ sigact_table[oldsig - 1]._sa_handler = TARGET_SIG_DFL; } @@ -540,7 +561,7 @@ static void force_sigsegv(int oldsig) #endif /* abort execution with signal */ -static void QEMU_NORETURN force_sig(int target_sig) +static void QEMU_NORETURN dump_core_and_abort(int target_sig) { CPUState *cpu = thread_cpu; CPUArchState *env = cpu->env_ptr; @@ -1181,7 +1202,7 @@ long do_sigreturn(CPUX86State *env) badframe: unlock_user_struct(frame, frame_addr, 0); force_sig(TARGET_SIGSEGV); - return 0; + return -TARGET_QEMU_ESIGRETURN; } long do_rt_sigreturn(CPUX86State *env) @@ -1212,7 +1233,7 @@ long do_rt_sigreturn(CPUX86State *env) badframe: unlock_user_struct(frame, frame_addr, 0); force_sig(TARGET_SIGSEGV); - return 0; + return -TARGET_QEMU_ESIGRETURN; } #elif defined(TARGET_AARCH64) @@ -1482,7 +1503,7 @@ long do_rt_sigreturn(CPUARMState *env) badframe: unlock_user_struct(frame, frame_addr, 0); force_sig(TARGET_SIGSEGV); - return 0; + return -TARGET_QEMU_ESIGRETURN; } long do_sigreturn(CPUARMState *env) @@ -2004,8 +2025,8 @@ static long do_sigreturn_v1(CPUARMState *env) return -TARGET_QEMU_ESIGRETURN; badframe: - force_sig(TARGET_SIGSEGV /* , current */); - return 0; + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; } static abi_ulong *restore_sigframe_v2_vfp(CPUARMState *env, abi_ulong *regspace) @@ -2131,8 +2152,8 @@ static long do_sigreturn_v2(CPUARMState *env) badframe: unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV /* , current */); - return 0; + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; } long do_sigreturn(CPUARMState *env) @@ -2185,8 +2206,8 @@ static long do_rt_sigreturn_v1(CPUARMState *env) badframe: unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV /* , current */); - return 0; + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; } static long do_rt_sigreturn_v2(CPUARMState *env) @@ -2218,8 +2239,8 @@ static long do_rt_sigreturn_v2(CPUARMState *env) badframe: unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV /* , current */); - return 0; + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; } long do_rt_sigreturn(CPUARMState *env) @@ -2553,6 +2574,7 @@ long do_sigreturn(CPUSPARCState *env) segv_and_exit: unlock_user_struct(sf, sf_addr, 0); force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; } long do_rt_sigreturn(CPUSPARCState *env) @@ -3110,8 +3132,8 @@ long do_sigreturn(CPUMIPSState *regs) return -TARGET_QEMU_ESIGRETURN; badframe: - force_sig(TARGET_SIGSEGV/*, current*/); - return 0; + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; } # endif /* O32 */ @@ -3207,8 +3229,8 @@ long do_rt_sigreturn(CPUMIPSState *env) return -TARGET_QEMU_ESIGRETURN; badframe: - force_sig(TARGET_SIGSEGV/*, current*/); - return 0; + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; } #elif defined(TARGET_SH4) @@ -3474,7 +3496,7 @@ long do_sigreturn(CPUSH4State *regs) badframe: unlock_user_struct(frame, frame_addr, 0); force_sig(TARGET_SIGSEGV); - return 0; + return -TARGET_QEMU_ESIGRETURN; } long do_rt_sigreturn(CPUSH4State *regs) @@ -3506,7 +3528,7 @@ long do_rt_sigreturn(CPUSH4State *regs) badframe: unlock_user_struct(frame, frame_addr, 0); force_sig(TARGET_SIGSEGV); - return 0; + return -TARGET_QEMU_ESIGRETURN; } #elif defined(TARGET_MICROBLAZE) @@ -3725,6 +3747,7 @@ long do_sigreturn(CPUMBState *env) return -TARGET_QEMU_ESIGRETURN; badframe: force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; } long do_rt_sigreturn(CPUMBState *env) @@ -3892,6 +3915,7 @@ long do_sigreturn(CPUCRISState *env) return -TARGET_QEMU_ESIGRETURN; badframe: force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; } long do_rt_sigreturn(CPUCRISState *env) @@ -4383,7 +4407,7 @@ long do_sigreturn(CPUS390XState *env) badframe: force_sig(TARGET_SIGSEGV); - return 0; + return -TARGET_QEMU_ESIGRETURN; } long do_rt_sigreturn(CPUS390XState *env) @@ -4414,7 +4438,7 @@ long do_rt_sigreturn(CPUS390XState *env) badframe: unlock_user_struct(frame, frame_addr, 0); force_sig(TARGET_SIGSEGV); - return 0; + return -TARGET_QEMU_ESIGRETURN; } #elif defined(TARGET_PPC) @@ -4973,7 +4997,7 @@ sigsegv: unlock_user_struct(sr, sr_addr, 1); unlock_user_struct(sc, sc_addr, 1); force_sig(TARGET_SIGSEGV); - return 0; + return -TARGET_QEMU_ESIGRETURN; } /* See arch/powerpc/kernel/signal_32.c. */ @@ -5028,7 +5052,7 @@ long do_rt_sigreturn(CPUPPCState *env) sigsegv: unlock_user_struct(rt_sf, rt_sf_addr, 1); force_sig(TARGET_SIGSEGV); - return 0; + return -TARGET_QEMU_ESIGRETURN; } #elif defined(TARGET_M68K) @@ -5358,7 +5382,7 @@ long do_sigreturn(CPUM68KState *env) badframe: force_sig(TARGET_SIGSEGV); - return 0; + return -TARGET_QEMU_ESIGRETURN; } long do_rt_sigreturn(CPUM68KState *env) @@ -5391,7 +5415,7 @@ long do_rt_sigreturn(CPUM68KState *env) badframe: unlock_user_struct(frame, frame_addr, 0); force_sig(TARGET_SIGSEGV); - return 0; + return -TARGET_QEMU_ESIGRETURN; } #elif defined(TARGET_ALPHA) @@ -5620,6 +5644,7 @@ long do_sigreturn(CPUAlphaState *env) badframe: force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; } long do_rt_sigreturn(CPUAlphaState *env) @@ -5649,6 +5674,7 @@ long do_rt_sigreturn(CPUAlphaState *env) badframe: unlock_user_struct(frame, frame_addr, 0); force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; } #elif defined(TARGET_TILEGX) @@ -5813,6 +5839,7 @@ long do_rt_sigreturn(CPUTLGState *env) badframe: unlock_user_struct(frame, frame_addr, 0); force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; } #else @@ -5879,12 +5906,12 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig, sig != TARGET_SIGURG && sig != TARGET_SIGWINCH && sig != TARGET_SIGCONT) { - force_sig(sig); + dump_core_and_abort(sig); } } else if (handler == TARGET_SIG_IGN) { /* ignore sig */ } else if (handler == TARGET_SIG_ERR) { - force_sig(sig); + dump_core_and_abort(sig); } else { /* compute the blocked signals during the handler execution */ sigset_t *blocked_set; From c4b3574402053a88612eab3b66a53825a00145a2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Jul 2016 16:44:50 +0100 Subject: [PATCH 22/26] linux-user: Implement force_sigsegv() via force_sig() Now that we have a force_sig() with the semantics we need, we can implement force_sigsegv() to call it rather than open-coding the call to queue_signal(). Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/signal.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index 60fda18117..900ee3515a 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -512,8 +512,7 @@ void signal_init(void) } } -#if !defined(TARGET_OPENRISC) && !defined(TARGET_UNICORE32) && \ - !defined(TARGET_X86_64) +#if !(defined(TARGET_X86_64) || defined(TARGET_UNICORE32)) /* Force a synchronously taken signal. The kernel force_sig() function * also forces the signal to "not blocked, not ignored", but for QEMU * that work is done in process_pending_signals(). @@ -531,9 +530,6 @@ static void force_sig(int sig) info._sifields._kill._uid = 0; queue_signal(env, info.si_signo, QEMU_SI_KILL, &info); } -#endif - -#if !(defined(TARGET_X86_64) || defined(TARGET_UNICORE32)) /* Force a SIGSEGV if we couldn't write to memory trying to set * up the signal frame. oldsig is the signal we were trying to handle @@ -541,22 +537,13 @@ static void force_sig(int sig) */ static void force_sigsegv(int oldsig) { - CPUState *cpu = thread_cpu; - CPUArchState *env = cpu->env_ptr; - target_siginfo_t info; - if (oldsig == SIGSEGV) { /* Make sure we don't try to deliver the signal again; this will * end up with handle_pending_signal() calling dump_core_and_abort(). */ sigact_table[oldsig - 1]._sa_handler = TARGET_SIG_DFL; } - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SI_KERNEL; - info._sifields._kill._pid = 0; - info._sifields._kill._uid = 0; - queue_signal(env, info.si_signo, QEMU_SI_KILL, &info); + force_sig(TARGET_SIGSEGV); } #endif From 7cfbd386b92e93fbfae033b9ac89a20d1fe72573 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 2 Aug 2016 18:41:26 +0100 Subject: [PATCH 23/26] linux-user: Remove unnecessary nptl_flags variable from do_fork() The 'nptl_flags' variable in do_fork() is set to a copy of 'flags', and then the CLONE_NPTL_FLAGS are cleared out of 'flags'. However the only effect of this is that the later check on "if (flags & CLONE_PARENT_SETTID)" is never true. Since we will already have done the setting of parent_tidptr in clone_func() in the child thread, we don't need to do it again. Delete the dead if() and the clearing of CLONE_NPTL_FLAGS from 'flags', and then use 'flags' where we were previously using 'nptl_flags', so we can delete the unnecessary variable. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 27ad6a2a6c..3b7b51f6c7 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6011,7 +6011,6 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, TaskState *ts; CPUState *new_cpu; CPUArchState *new_env; - unsigned int nptl_flags; sigset_t sigmask; /* Emulate vfork() with fork() */ @@ -6034,15 +6033,14 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, ts->bprm = parent_ts->bprm; ts->info = parent_ts->info; ts->signal_mask = parent_ts->signal_mask; - nptl_flags = flags; - flags &= ~CLONE_NPTL_FLAGS2; - if (nptl_flags & CLONE_CHILD_CLEARTID) { + if (flags & CLONE_CHILD_CLEARTID) { ts->child_tidptr = child_tidptr; } - if (nptl_flags & CLONE_SETTLS) + if (flags & CLONE_SETTLS) { cpu_set_tls (new_env, newtls); + } /* Grab a mutex so that thread setup appears atomic. */ pthread_mutex_lock(&clone_lock); @@ -6052,10 +6050,12 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, pthread_mutex_lock(&info.mutex); pthread_cond_init(&info.cond, NULL); info.env = new_env; - if (nptl_flags & CLONE_CHILD_SETTID) + if (flags & CLONE_CHILD_SETTID) { info.child_tidptr = child_tidptr; - if (nptl_flags & CLONE_PARENT_SETTID) + } + if (flags & CLONE_PARENT_SETTID) { info.parent_tidptr = parent_tidptr; + } ret = pthread_attr_init(&attr); ret = pthread_attr_setstacksize(&attr, NEW_STACK_SIZE); @@ -6074,8 +6074,6 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, /* Wait for the child to initialize. */ pthread_cond_wait(&info.cond, &info.mutex); ret = info.tid; - if (flags & CLONE_PARENT_SETTID) - put_user_u32(ret, parent_tidptr); } else { ret = -1; } From 5ea2fc84da1bffce749c9d0848f5336def2818bb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 2 Aug 2016 18:41:27 +0100 Subject: [PATCH 24/26] linux-user: Sanity check clone flags We currently make no checks on the flags passed to the clone syscall, which means we will not fail clone attempts which ask for features that we can't implement. Add sanity checking of the flags to clone (which we were already doing in the "this is a fork" path, but not for the "this is a new thread" path), tidy up the checking in the fork path to match it, and check that the fork case isn't trying to specify a custom termination signal. This is helpful in causing some LTP test cases to fail cleanly rather than behaving bizarrely when we let the clone succeed but didn't provide the semantics requested by the flags. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 66 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 3b7b51f6c7..d7e4d9ff2f 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -112,8 +112,56 @@ int __clone2(int (*fn)(void *), void *child_stack_base, #include "qemu.h" -#define CLONE_NPTL_FLAGS2 (CLONE_SETTLS | \ - CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID) +#ifndef CLONE_IO +#define CLONE_IO 0x80000000 /* Clone io context */ +#endif + +/* We can't directly call the host clone syscall, because this will + * badly confuse libc (breaking mutexes, for example). So we must + * divide clone flags into: + * * flag combinations that look like pthread_create() + * * flag combinations that look like fork() + * * flags we can implement within QEMU itself + * * flags we can't support and will return an error for + */ +/* For thread creation, all these flags must be present; for + * fork, none must be present. + */ +#define CLONE_THREAD_FLAGS \ + (CLONE_VM | CLONE_FS | CLONE_FILES | \ + CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM) + +/* These flags are ignored: + * CLONE_DETACHED is now ignored by the kernel; + * CLONE_IO is just an optimisation hint to the I/O scheduler + */ +#define CLONE_IGNORED_FLAGS \ + (CLONE_DETACHED | CLONE_IO) + +/* Flags for fork which we can implement within QEMU itself */ +#define CLONE_OPTIONAL_FORK_FLAGS \ + (CLONE_SETTLS | CLONE_PARENT_SETTID | \ + CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID) + +/* Flags for thread creation which we can implement within QEMU itself */ +#define CLONE_OPTIONAL_THREAD_FLAGS \ + (CLONE_SETTLS | CLONE_PARENT_SETTID | \ + CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | CLONE_PARENT) + +#define CLONE_INVALID_FORK_FLAGS \ + (~(CSIGNAL | CLONE_OPTIONAL_FORK_FLAGS | CLONE_IGNORED_FLAGS)) + +#define CLONE_INVALID_THREAD_FLAGS \ + (~(CSIGNAL | CLONE_THREAD_FLAGS | CLONE_OPTIONAL_THREAD_FLAGS | \ + CLONE_IGNORED_FLAGS)) + +/* CLONE_VFORK is special cased early in do_fork(). The other flag bits + * have almost all been allocated. We cannot support any of + * CLONE_NEWNS, CLONE_NEWCGROUP, CLONE_NEWUTS, CLONE_NEWIPC, + * CLONE_NEWUSER, CLONE_NEWPID, CLONE_NEWNET, CLONE_PTRACE, CLONE_UNTRACED. + * The checks against the invalid thread masks above will catch these. + * (The one remaining unallocated bit is 0x1000 which used to be CLONE_PID.) + */ //#define DEBUG /* Define DEBUG_ERESTARTSYS to force every syscall to be restarted @@ -6013,6 +6061,8 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, CPUArchState *new_env; sigset_t sigmask; + flags &= ~CLONE_IGNORED_FLAGS; + /* Emulate vfork() with fork() */ if (flags & CLONE_VFORK) flags &= ~(CLONE_VFORK | CLONE_VM); @@ -6022,6 +6072,11 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, new_thread_info info; pthread_attr_t attr; + if (((flags & CLONE_THREAD_FLAGS) != CLONE_THREAD_FLAGS) || + (flags & CLONE_INVALID_THREAD_FLAGS)) { + return -TARGET_EINVAL; + } + ts = g_new0(TaskState, 1); init_task_state(ts); /* we create a new CPU instance. */ @@ -6083,7 +6138,12 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, pthread_mutex_unlock(&clone_lock); } else { /* if no CLONE_VM, we consider it is a fork */ - if ((flags & ~(CSIGNAL | CLONE_NPTL_FLAGS2)) != 0) { + if (flags & CLONE_INVALID_FORK_FLAGS) { + return -TARGET_EINVAL; + } + + /* We can't support custom termination signals */ + if ((flags & CSIGNAL) != TARGET_SIGCHLD) { return -TARGET_EINVAL; } From 45eafb4d32ced9ff1dcb3800c89f8beaf47b61cc Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Fri, 9 Sep 2016 19:35:58 +0100 Subject: [PATCH 25/26] linux-user: Fix incorrect offset of tuc_stack in ARM do_sigframe_return_v2 struct target_ucontext_v2 is not at the begining of the signal frame, therefore do_sigaltstack was being passed bogus arguments. As the offset depends on the type of signal frame fixed by passing in the beginning of the context from do_sigreturn_v2 and do_rt_sigreturn_v2. Suggested-by: Peter Maydell Reviewed-by: Peter Maydell Signed-off-by: Timothy Edward Baldwin Signed-off-by: Riku Voipio --- linux-user/signal.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index 900ee3515a..e4eea697b4 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -2071,7 +2071,8 @@ static abi_ulong *restore_sigframe_v2_iwmmxt(CPUARMState *env, return (abi_ulong*)(iwmmxtframe + 1); } -static int do_sigframe_return_v2(CPUARMState *env, target_ulong frame_addr, +static int do_sigframe_return_v2(CPUARMState *env, + target_ulong context_addr, struct target_ucontext_v2 *uc) { sigset_t host_set; @@ -2098,8 +2099,11 @@ static int do_sigframe_return_v2(CPUARMState *env, target_ulong frame_addr, } } - if (do_sigaltstack(frame_addr + offsetof(struct target_ucontext_v2, tuc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) + if (do_sigaltstack(context_addr + + offsetof(struct target_ucontext_v2, tuc_stack), + 0, get_sp_from_cpustate(env)) == -EFAULT) { return 1; + } #if 0 /* Send SIGTRAP if we're single-stepping */ @@ -2130,7 +2134,10 @@ static long do_sigreturn_v2(CPUARMState *env) goto badframe; } - if (do_sigframe_return_v2(env, frame_addr, &frame->uc)) { + if (do_sigframe_return_v2(env, + frame_addr + + offsetof(struct sigframe_v2, uc), + &frame->uc)) { goto badframe; } @@ -2217,7 +2224,10 @@ static long do_rt_sigreturn_v2(CPUARMState *env) goto badframe; } - if (do_sigframe_return_v2(env, frame_addr, &frame->uc)) { + if (do_sigframe_return_v2(env, + frame_addr + + offsetof(struct rt_sigframe_v2, uc), + &frame->uc)) { goto badframe; } From 5457dc9e37fe0a29989bd64306c63941074864ce Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 8 Jul 2016 01:17:27 +0200 Subject: [PATCH 26/26] linux-user: fix TARGET_NR_select TARGET_NR_select can have three different implementations: 1- to always return -ENOSYS microblaze, ppc, ppc64 -> TARGET_WANT_NI_OLD_SELECT 2- to take parameters from a structure pointed by arg1 (kernel sys_old_select) i386, arm, m68k -> TARGET_WANT_OLD_SYS_SELECT 3- to take parameters from arg[1-5] (kernel sys_select) x86_64, alpha, s390x, cris, sparc, sparc64 Some (new) architectures don't define NR_select, 4- but only NR__newselect with sys_select: mips, mips64, sh 5- don't define NR__newselect, and use pselect6 syscall: aarch64, openrisc, tilegx, unicore32 Reported-by: Timothy Pearson Reported-by: Allan Wirth Suggested-by: Peter Maydell Reviewed-by: Peter Maydell Signed-off-by: Laurent Vivier Signed-off-by: Riku Voipio --- linux-user/arm/target_syscall.h | 1 + linux-user/i386/target_syscall.h | 1 + linux-user/m68k/target_syscall.h | 2 ++ linux-user/microblaze/target_syscall.h | 2 ++ linux-user/openrisc/syscall_nr.h | 2 -- linux-user/ppc/target_syscall.h | 1 + linux-user/sh4/syscall_nr.h | 2 +- linux-user/syscall.c | 48 +++++++++++++++++--------- linux-user/tilegx/syscall_nr.h | 1 - 9 files changed, 39 insertions(+), 21 deletions(-) diff --git a/linux-user/arm/target_syscall.h b/linux-user/arm/target_syscall.h index 0879b4d4a8..94e2a42cb2 100644 --- a/linux-user/arm/target_syscall.h +++ b/linux-user/arm/target_syscall.h @@ -32,6 +32,7 @@ struct target_pt_regs { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_MLOCKALL_MCL_CURRENT 1 #define TARGET_MLOCKALL_MCL_FUTURE 2 +#define TARGET_WANT_OLD_SYS_SELECT #define TARGET_FORCE_SHMLBA diff --git a/linux-user/i386/target_syscall.h b/linux-user/i386/target_syscall.h index b4e895fd9c..2854758134 100644 --- a/linux-user/i386/target_syscall.h +++ b/linux-user/i386/target_syscall.h @@ -153,5 +153,6 @@ struct target_vm86plus_struct { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_MLOCKALL_MCL_CURRENT 1 #define TARGET_MLOCKALL_MCL_FUTURE 2 +#define TARGET_WANT_OLD_SYS_SELECT #endif /* I386_TARGET_SYSCALL_H */ diff --git a/linux-user/m68k/target_syscall.h b/linux-user/m68k/target_syscall.h index db2be4f101..632ee4fcf8 100644 --- a/linux-user/m68k/target_syscall.h +++ b/linux-user/m68k/target_syscall.h @@ -24,6 +24,8 @@ struct target_pt_regs { #define TARGET_MLOCKALL_MCL_CURRENT 1 #define TARGET_MLOCKALL_MCL_FUTURE 2 +#define TARGET_WANT_OLD_SYS_SELECT + void do_m68k_simcall(CPUM68KState *, int); #endif /* M68K_TARGET_SYSCALL_H */ diff --git a/linux-user/microblaze/target_syscall.h b/linux-user/microblaze/target_syscall.h index 0b6980c899..4141cbaa5e 100644 --- a/linux-user/microblaze/target_syscall.h +++ b/linux-user/microblaze/target_syscall.h @@ -53,4 +53,6 @@ struct target_pt_regs { #define TARGET_MLOCKALL_MCL_CURRENT 1 #define TARGET_MLOCKALL_MCL_FUTURE 2 +#define TARGET_WANT_NI_OLD_SELECT + #endif diff --git a/linux-user/openrisc/syscall_nr.h b/linux-user/openrisc/syscall_nr.h index 6b1c7d265e..04059d020c 100644 --- a/linux-user/openrisc/syscall_nr.h +++ b/linux-user/openrisc/syscall_nr.h @@ -459,8 +459,6 @@ #define TARGET_NR_getdents 1065 #define __ARCH_WANT_SYS_GETDENTS #define TARGET_NR_futimesat 1066 -#define TARGET_NR_select 1067 -#define __ARCH_WANT_SYS_SELECT #define TARGET_NR_poll 1068 #define TARGET_NR_epoll_wait 1069 #define TARGET_NR_ustat 1070 diff --git a/linux-user/ppc/target_syscall.h b/linux-user/ppc/target_syscall.h index a8662f4856..afc0570410 100644 --- a/linux-user/ppc/target_syscall.h +++ b/linux-user/ppc/target_syscall.h @@ -74,5 +74,6 @@ struct target_revectored_struct { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_MLOCKALL_MCL_CURRENT 0x2000 #define TARGET_MLOCKALL_MCL_FUTURE 0x4000 +#define TARGET_WANT_NI_OLD_SELECT #endif /* PPC_TARGET_SYSCALL_H */ diff --git a/linux-user/sh4/syscall_nr.h b/linux-user/sh4/syscall_nr.h index 50099846d2..e99f73589d 100644 --- a/linux-user/sh4/syscall_nr.h +++ b/linux-user/sh4/syscall_nr.h @@ -84,7 +84,7 @@ #define TARGET_NR_settimeofday 79 #define TARGET_NR_getgroups 80 #define TARGET_NR_setgroups 81 -#define TARGET_NR_select 82 + /* 82 was sys_oldselect */ #define TARGET_NR_symlink 83 #define TARGET_NR_oldlstat 84 #define TARGET_NR_readlink 85 diff --git a/linux-user/syscall.c b/linux-user/syscall.c index d7e4d9ff2f..7aa2c1d720 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -1444,6 +1444,29 @@ static abi_long do_select(int n, return ret; } + +#if defined(TARGET_WANT_OLD_SYS_SELECT) +static abi_long do_old_select(abi_ulong arg1) +{ + struct target_sel_arg_struct *sel; + abi_ulong inp, outp, exp, tvp; + long nsel; + + if (!lock_user_struct(VERIFY_READ, sel, arg1, 1)) { + return -TARGET_EFAULT; + } + + nsel = tswapal(sel->n); + inp = tswapal(sel->inp); + outp = tswapal(sel->outp); + exp = tswapal(sel->exp); + tvp = tswapal(sel->tvp); + + unlock_user_struct(sel, arg1, 0); + + return do_select(nsel, inp, outp, exp, tvp); +} +#endif #endif static abi_long do_pipe2(int host_pipe[], int flags) @@ -8668,24 +8691,15 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; #if defined(TARGET_NR_select) case TARGET_NR_select: -#if defined(TARGET_S390X) || defined(TARGET_ALPHA) - ret = do_select(arg1, arg2, arg3, arg4, arg5); +#if defined(TARGET_WANT_NI_OLD_SELECT) + /* some architectures used to have old_select here + * but now ENOSYS it. + */ + ret = -TARGET_ENOSYS; +#elif defined(TARGET_WANT_OLD_SYS_SELECT) + ret = do_old_select(arg1); #else - { - struct target_sel_arg_struct *sel; - abi_ulong inp, outp, exp, tvp; - long nsel; - - if (!lock_user_struct(VERIFY_READ, sel, arg1, 1)) - goto efault; - nsel = tswapal(sel->n); - inp = tswapal(sel->inp); - outp = tswapal(sel->outp); - exp = tswapal(sel->exp); - tvp = tswapal(sel->tvp); - unlock_user_struct(sel, arg1, 0); - ret = do_select(nsel, inp, outp, exp, tvp); - } + ret = do_select(arg1, arg2, arg3, arg4, arg5); #endif break; #endif diff --git a/linux-user/tilegx/syscall_nr.h b/linux-user/tilegx/syscall_nr.h index 8e30cd1ae9..c104b94230 100644 --- a/linux-user/tilegx/syscall_nr.h +++ b/linux-user/tilegx/syscall_nr.h @@ -311,7 +311,6 @@ #define TARGET_NR_creat 1064 #define TARGET_NR_getdents 1065 #define TARGET_NR_futimesat 1066 -#define TARGET_NR_select 1067 #define TARGET_NR_poll 1068 #define TARGET_NR_epoll_wait 1069 #define TARGET_NR_ustat 1070