From 7174970a94df10ee84143edc7c94a2472d654604 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Dec 2017 13:52:55 +0000 Subject: linux-user: Fix length calculations in host_to_target_cmsg() The handling of length calculations in host_to_target_cmsg() was rather confused: * when checking for whether the target cmsg header fit in the remaining buffer, we were using the host struct size, not the target size * we were setting tgt_len to "target payload + header length" but then using it as if it were the target payload length alone * in various message type cases we weren't handling the possibility that host or target buffers were truncated Fix these problems. The second one in particular is liable to result in us overrunning the guest provided buffer, since we will try to convert more data than is actually present. Fixes: https://bugs.launchpad.net/qemu/+bug/1701808 Reported-by: Bruno Haible Signed-off-by: Peter Maydell Message-Id: <1513345976-22958-2-git-send-email-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/syscall.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 11c9116c4a..a1b9772a85 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -1782,7 +1782,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, * to the guest via the CTRUNC bit), unlike truncation * in target_to_host_cmsg, which is a QEMU bug. */ - if (msg_controllen < sizeof(struct cmsghdr)) { + if (msg_controllen < sizeof(struct target_cmsghdr)) { target_msgh->msg_flags |= tswap32(MSG_CTRUNC); break; } @@ -1794,8 +1794,6 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, } target_cmsg->cmsg_type = tswap32(cmsg->cmsg_type); - tgt_len = TARGET_CMSG_LEN(len); - /* Payload types which need a different size of payload on * the target must adjust tgt_len here. */ @@ -1809,12 +1807,13 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, break; } default: + tgt_len = len; break; } - if (msg_controllen < tgt_len) { + if (msg_controllen < TARGET_CMSG_LEN(tgt_len)) { target_msgh->msg_flags |= tswap32(MSG_CTRUNC); - tgt_len = msg_controllen; + tgt_len = msg_controllen - sizeof(struct target_cmsghdr); } /* We must now copy-and-convert len bytes of payload @@ -1875,6 +1874,10 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, uint32_t *v = (uint32_t *)data; uint32_t *t_int = (uint32_t *)target_data; + if (len != sizeof(uint32_t) || + tgt_len != sizeof(uint32_t)) { + goto unimplemented; + } __put_user(*v, t_int); break; } @@ -1888,6 +1891,10 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, struct errhdr_t *target_errh = (struct errhdr_t *)target_data; + if (len != sizeof(struct errhdr_t) || + tgt_len != sizeof(struct errhdr_t)) { + goto unimplemented; + } __put_user(errh->ee.ee_errno, &target_errh->ee.ee_errno); __put_user(errh->ee.ee_origin, &target_errh->ee.ee_origin); __put_user(errh->ee.ee_type, &target_errh->ee.ee_type); @@ -1911,6 +1918,10 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, uint32_t *v = (uint32_t *)data; uint32_t *t_int = (uint32_t *)target_data; + if (len != sizeof(uint32_t) || + tgt_len != sizeof(uint32_t)) { + goto unimplemented; + } __put_user(*v, t_int); break; } @@ -1924,6 +1935,10 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, struct errhdr6_t *target_errh = (struct errhdr6_t *)target_data; + if (len != sizeof(struct errhdr6_t) || + tgt_len != sizeof(struct errhdr6_t)) { + goto unimplemented; + } __put_user(errh->ee.ee_errno, &target_errh->ee.ee_errno); __put_user(errh->ee.ee_origin, &target_errh->ee.ee_origin); __put_user(errh->ee.ee_type, &target_errh->ee.ee_type); @@ -1950,8 +1965,8 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, } } - target_cmsg->cmsg_len = tswapal(tgt_len); - tgt_space = TARGET_CMSG_SPACE(len); + target_cmsg->cmsg_len = tswapal(TARGET_CMSG_LEN(tgt_len)); + tgt_space = TARGET_CMSG_SPACE(tgt_len); if (msg_controllen < tgt_space) { tgt_space = msg_controllen; } -- cgit 1.4.1 From ad762b990fa9da53e203b934583838d4dd371e20 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Dec 2017 13:52:56 +0000 Subject: linux-user: Don't use CMSG_ALIGN(sizeof struct cmsghdr) The Linux struct cmsghdr is already guaranteed to be sufficiently aligned that CMSG_ALIGN(sizeof struct cmsghdr) is always equal to sizeof struct cmsghdr. Stop doing the unnecessary alignment arithmetic for host and target cmsghdr. This follows kernel commit 1ff8cebf49ed9e9ca2 and brings our TARGET_CMSG_* macros back into line with the kernel ones, as well as making them easier to understand. Signed-off-by: Peter Maydell Reviewed-by: Laurent Vivier Message-Id: <1513345976-22958-3-git-send-email-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/syscall.c | 4 ++-- linux-user/syscall_defs.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index a1b9772a85..39553c81b6 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -1692,7 +1692,7 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, void *target_data = TARGET_CMSG_DATA(target_cmsg); int len = tswapal(target_cmsg->cmsg_len) - - TARGET_CMSG_ALIGN(sizeof (struct target_cmsghdr)); + - sizeof(struct target_cmsghdr); space += CMSG_SPACE(len); if (space > msgh->msg_controllen) { @@ -1773,7 +1773,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, void *data = CMSG_DATA(cmsg); void *target_data = TARGET_CMSG_DATA(target_cmsg); - int len = cmsg->cmsg_len - CMSG_ALIGN(sizeof (struct cmsghdr)); + int len = cmsg->cmsg_len - sizeof(struct cmsghdr); int tgt_len, tgt_space; /* We never copy a half-header but may copy half-data; diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index bec3680b94..a35c52a60a 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -303,9 +303,9 @@ struct target_cmsghdr { __target_cmsg_nxthdr(mhdr, cmsg, cmsg_start) #define TARGET_CMSG_ALIGN(len) (((len) + sizeof (abi_long) - 1) \ & (size_t) ~(sizeof (abi_long) - 1)) -#define TARGET_CMSG_SPACE(len) (TARGET_CMSG_ALIGN (len) \ - + TARGET_CMSG_ALIGN (sizeof (struct target_cmsghdr))) -#define TARGET_CMSG_LEN(len) (TARGET_CMSG_ALIGN (sizeof (struct target_cmsghdr)) + (len)) +#define TARGET_CMSG_SPACE(len) (sizeof(struct target_cmsghdr) + \ + TARGET_CMSG_ALIGN(len)) +#define TARGET_CMSG_LEN(len) (sizeof(struct target_cmsghdr) + (len)) static __inline__ struct target_cmsghdr * __target_cmsg_nxthdr(struct target_msghdr *__mhdr, -- cgit 1.4.1 From 10fa993aae539fa8d0da1da17169e05f1dfb4f95 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Dec 2017 15:18:00 +0000 Subject: linux-user: Translate flags argument to dup3 syscall The third argument to dup3() is a flags word which may be O_CLOEXEC. We weren't translating this flag from target to host value, which meant that if the target used a different value from the host (eg sparc guest and x86 host) the dup3() call would fail EINVAL. Do the correct translation. Fixes: https://bugs.launchpad.net/qemu/+bug/1704658 Reported-by: Bruno Haible Signed-off-by: Peter Maydell Reviewed-by: Laurent Vivier Message-Id: <1513351080-25917-1-git-send-email-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/syscall.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 39553c81b6..41ded90ee6 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -8490,11 +8490,19 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #if defined(CONFIG_DUP3) && defined(TARGET_NR_dup3) case TARGET_NR_dup3: - ret = get_errno(dup3(arg1, arg2, arg3)); + { + int host_flags; + + if ((arg3 & ~TARGET_O_CLOEXEC) != 0) { + return -EINVAL; + } + host_flags = target_to_host_bitmask(arg3, fcntl_flags_tbl); + ret = get_errno(dup3(arg1, arg2, host_flags)); if (ret >= 0) { fd_trans_dup(arg1, arg2); } break; + } #endif #ifdef TARGET_NR_getppid /* not on alpha */ case TARGET_NR_getppid: -- cgit 1.4.1 From 2e0a8713bd6804ce49044915ba9cd5bd7148de77 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Tue, 9 Jan 2018 21:16:43 +0100 Subject: linux-user: Fix sched_get/setaffinity conversion sched_get/setaffinity linux-user syscalls were missing conversions for little/big endian, which is hairy since longs may not be the same size either. For simplicity, this just introduces loops to convert bit by bit like is done for select. Signed-off-by: Samuel Thibault Reviewed-by: Laurent Vivier Message-Id: <20180109201643.1479-1-samuel.thibault@ens-lyon.org> Signed-off-by: Laurent Vivier --- linux-user/syscall.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 8 deletions(-) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 41ded90ee6..143e4a959d 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -7731,6 +7731,73 @@ static TargetFdTrans target_inotify_trans = { }; #endif +static int target_to_host_cpu_mask(unsigned long *host_mask, + size_t host_size, + abi_ulong target_addr, + size_t target_size) +{ + unsigned target_bits = sizeof(abi_ulong) * 8; + unsigned host_bits = sizeof(*host_mask) * 8; + abi_ulong *target_mask; + unsigned i, j; + + assert(host_size >= target_size); + + target_mask = lock_user(VERIFY_READ, target_addr, target_size, 1); + if (!target_mask) { + return -TARGET_EFAULT; + } + memset(host_mask, 0, host_size); + + for (i = 0 ; i < target_size / sizeof(abi_ulong); i++) { + unsigned bit = i * target_bits; + abi_ulong val; + + __get_user(val, &target_mask[i]); + for (j = 0; j < target_bits; j++, bit++) { + if (val & (1UL << j)) { + host_mask[bit / host_bits] |= 1UL << (bit % host_bits); + } + } + } + + unlock_user(target_mask, target_addr, 0); + return 0; +} + +static int host_to_target_cpu_mask(const unsigned long *host_mask, + size_t host_size, + abi_ulong target_addr, + size_t target_size) +{ + unsigned target_bits = sizeof(abi_ulong) * 8; + unsigned host_bits = sizeof(*host_mask) * 8; + abi_ulong *target_mask; + unsigned i, j; + + assert(host_size >= target_size); + + target_mask = lock_user(VERIFY_WRITE, target_addr, target_size, 0); + if (!target_mask) { + return -TARGET_EFAULT; + } + + for (i = 0 ; i < target_size / sizeof(abi_ulong); i++) { + unsigned bit = i * target_bits; + abi_ulong val = 0; + + for (j = 0; j < target_bits; j++, bit++) { + if (host_mask[bit / host_bits] & (1UL << (bit % host_bits))) { + val |= 1UL << j; + } + } + __put_user(val, &target_mask[i]); + } + + unlock_user(target_mask, target_addr, target_size); + return 0; +} + /* do_syscall() should always have a single exit point at the end so that actions, such as logging of syscall results, can be performed. All errnos that do_syscall() returns must be -TARGET_. */ @@ -10376,6 +10443,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, mask_size = (arg2 + (sizeof(*mask) - 1)) & ~(sizeof(*mask) - 1); mask = alloca(mask_size); + memset(mask, 0, mask_size); ret = get_errno(sys_sched_getaffinity(arg1, mask_size, mask)); if (!is_error(ret)) { @@ -10395,9 +10463,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = arg2; } - if (copy_to_user(arg3, mask, ret)) { - goto efault; - } + ret = host_to_target_cpu_mask(mask, mask_size, arg3, arg2); } } break; @@ -10415,13 +10481,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; } mask_size = (arg2 + (sizeof(*mask) - 1)) & ~(sizeof(*mask) - 1); - mask = alloca(mask_size); - if (!lock_user_struct(VERIFY_READ, p, arg3, 1)) { - goto efault; + + ret = target_to_host_cpu_mask(mask, mask_size, arg3, arg2); + if (ret) { + break; } - memcpy(mask, p, arg2); - unlock_user_struct(p, arg2, 0); ret = get_errno(sys_sched_setaffinity(arg1, mask_size, mask)); } -- cgit 1.4.1 From b827c3ed30e4777ab9bd91cb240252a4815b3322 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Fri, 12 Jan 2018 09:14:35 +0100 Subject: linux-user: Add getcpu() support Signed-off-by: Samuel Thibault Reviewed-by: Laurent Vivier Message-Id: <20180112081435.21299-1-samuel.thibault@ens-lyon.org> Signed-off-by: Laurent Vivier --- 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 143e4a959d..104408c050 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -296,6 +296,8 @@ _syscall3(int, sys_sched_getaffinity, pid_t, pid, unsigned int, len, #define __NR_sys_sched_setaffinity __NR_sched_setaffinity _syscall3(int, sys_sched_setaffinity, pid_t, pid, unsigned int, len, unsigned long *, user_mask_ptr); +#define __NR_sys_getcpu __NR_getcpu +_syscall3(int, sys_getcpu, unsigned *, cpu, unsigned *, node, void *, tcache); _syscall4(int, reboot, int, magic1, int, magic2, unsigned int, cmd, void *, arg); _syscall2(int, capget, struct __user_cap_header_struct *, header, @@ -10491,6 +10493,23 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(sys_sched_setaffinity(arg1, mask_size, mask)); } break; + case TARGET_NR_getcpu: + { + unsigned cpu, node; + ret = get_errno(sys_getcpu(arg1 ? &cpu : NULL, + arg2 ? &node : NULL, + NULL)); + if (is_error(ret)) { + goto fail; + } + if (arg1 && put_user_u32(cpu, arg1)) { + goto efault; + } + if (arg2 && put_user_u32(node, arg2)) { + goto efault; + } + } + break; case TARGET_NR_sched_setparam: { struct sched_param *target_schp; -- cgit 1.4.1 From 95d0307cc10ca3df879c1be519f1ad650efb20a8 Mon Sep 17 00:00:00 2001 From: Andreas Schwab Date: Tue, 23 Jan 2018 11:53:31 +0100 Subject: linux-user: implement renameat2 This is needed for new architectures like RISC-V which do not provide any other rename-like syscall. Signed-off-by: Andreas Schwab Reviewed-by: Laurent Vivier Message-Id: Signed-off-by: Laurent Vivier --- linux-user/syscall.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'linux-user/syscall.c') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 104408c050..74378947f0 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -600,6 +600,24 @@ static int sys_utimensat(int dirfd, const char *pathname, #endif #endif /* TARGET_NR_utimensat */ +#ifdef TARGET_NR_renameat2 +#if defined(__NR_renameat2) +#define __NR_sys_renameat2 __NR_renameat2 +_syscall5(int, sys_renameat2, int, oldfd, const char *, old, int, newfd, + const char *, new, unsigned int, flags) +#else +static int sys_renameat2(int oldfd, const char *old, + int newfd, const char *new, int flags) +{ + if (flags == 0) { + return renameat(oldfd, old, newfd, new); + } + errno = ENOSYS; + return -1; +} +#endif +#endif /* TARGET_NR_renameat2 */ + #ifdef CONFIG_INOTIFY #include @@ -8426,6 +8444,22 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif +#if defined(TARGET_NR_renameat2) + case TARGET_NR_renameat2: + { + void *p2; + p = lock_user_string(arg2); + p2 = lock_user_string(arg4); + if (!p || !p2) { + ret = -TARGET_EFAULT; + } else { + ret = get_errno(sys_renameat2(arg1, p, arg3, p2, arg5)); + } + unlock_user(p2, arg4, 0); + unlock_user(p, arg2, 0); + } + break; +#endif #ifdef TARGET_NR_mkdir case TARGET_NR_mkdir: if (!(p = lock_user_string(arg1))) -- cgit 1.4.1