*/
#define _ATFILE_SOURCE
#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "qemu/path.h"
#include <elf.h>
#include <endian.h>
#include <grp.h>
#include <linux/route.h>
#include <linux/filter.h>
#include <linux/blkpg.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/audit.h>
#include "linux_loop.h"
#include "uname.h"
CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)
//#define DEBUG
+/* Define DEBUG_ERESTARTSYS to force every syscall to be restarted
+ * once. This exercises the codepaths for restart.
+ */
+//#define DEBUG_ERESTARTSYS
//#include <linux/msdos_fs.h>
#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct linux_dirent [2])
#if defined(TARGET_NR_ioprio_set) && defined(__NR_ioprio_set)
_syscall3(int, ioprio_set, int, which, int, who, int, ioprio)
#endif
+#if defined(TARGET_NR_getrandom) && defined(__NR_getrandom)
+_syscall3(int, getrandom, void *, buf, size_t, buflen, unsigned int, flags)
+#endif
static bitmask_transtbl fcntl_flags_tbl[] = {
{ TARGET_O_ACCMODE, TARGET_O_WRONLY, O_ACCMODE, O_WRONLY, },
static unsigned int target_fd_max;
+static TargetFdDataFunc fd_trans_target_to_host_data(int fd)
+{
+ if (fd >= 0 && fd < target_fd_max && target_fd_trans[fd]) {
+ return target_fd_trans[fd]->target_to_host_data;
+ }
+ return NULL;
+}
+
static TargetFdDataFunc fd_trans_host_to_target_data(int fd)
{
if (fd >= 0 && fd < target_fd_max && target_fd_trans[fd]) {
return strlen(buf)+1;
}
-static int sys_openat(int dirfd, const char *pathname, int flags, mode_t mode)
-{
- /*
- * open(2) has extra parameter 'mode' when called with
- * flag O_CREAT.
- */
- if ((flags & O_CREAT) != 0) {
- return (openat(dirfd, pathname, flags, mode));
- }
- return (openat(dirfd, pathname, flags));
-}
-
#ifdef TARGET_NR_utimensat
#ifdef CONFIG_UTIMENSAT
static int sys_utimensat(int dirfd, const char *pathname,
size_t, sigsetsize)
#endif
-#if defined(TARGET_NR_pselect6)
-#ifndef __NR_pselect6
-# define __NR_pselect6 -1
-#endif
-#define __NR_sys_pselect6 __NR_pselect6
-_syscall6(int, sys_pselect6, int, nfds, fd_set *, readfds, fd_set *, writefds,
- fd_set *, exceptfds, struct timespec *, timeout, void *, sig);
-#endif
-
#if defined(TARGET_NR_prlimit64)
#ifndef __NR_prlimit64
# define __NR_prlimit64 -1
static inline int host_to_target_errno(int err)
{
- if(host_to_target_errno_table[err])
+ if (err >= 0 && err < ERRNO_TABLE_SIZE &&
+ host_to_target_errno_table[err]) {
return host_to_target_errno_table[err];
+ }
return err;
}
static inline int target_to_host_errno(int err)
{
- if (target_to_host_errno_table[err])
+ if (err >= 0 && err < ERRNO_TABLE_SIZE &&
+ target_to_host_errno_table[err]) {
return target_to_host_errno_table[err];
+ }
return err;
}
return strerror(target_to_host_errno(err));
}
+#define safe_syscall0(type, name) \
+static type safe_##name(void) \
+{ \
+ return safe_syscall(__NR_##name); \
+}
+
+#define safe_syscall1(type, name, type1, arg1) \
+static type safe_##name(type1 arg1) \
+{ \
+ return safe_syscall(__NR_##name, arg1); \
+}
+
+#define safe_syscall2(type, name, type1, arg1, type2, arg2) \
+static type safe_##name(type1 arg1, type2 arg2) \
+{ \
+ return safe_syscall(__NR_##name, arg1, arg2); \
+}
+
+#define safe_syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \
+static type safe_##name(type1 arg1, type2 arg2, type3 arg3) \
+{ \
+ return safe_syscall(__NR_##name, arg1, arg2, arg3); \
+}
+
+#define safe_syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \
+ type4, arg4) \
+static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
+{ \
+ return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4); \
+}
+
+#define safe_syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \
+ type4, arg4, type5, arg5) \
+static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5) \
+{ \
+ return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5); \
+}
+
+#define safe_syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \
+ type4, arg4, type5, arg5, type6, arg6) \
+static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5, type6 arg6) \
+{ \
+ return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5, arg6); \
+}
+
+safe_syscall3(ssize_t, read, int, fd, void *, buff, size_t, count)
+safe_syscall3(ssize_t, write, int, fd, const void *, buff, size_t, count)
+safe_syscall4(int, openat, int, dirfd, const char *, pathname, \
+ int, flags, mode_t, mode)
+safe_syscall4(pid_t, wait4, pid_t, pid, int *, status, int, options, \
+ struct rusage *, rusage)
+safe_syscall5(int, waitid, idtype_t, idtype, id_t, id, siginfo_t *, infop, \
+ int, options, struct rusage *, rusage)
+safe_syscall3(int, execve, const char *, filename, char **, argv, char **, envp)
+safe_syscall6(int, pselect6, int, nfds, fd_set *, readfds, fd_set *, writefds, \
+ fd_set *, exceptfds, struct timespec *, timeout, void *, sig)
+safe_syscall6(int,futex,int *,uaddr,int,op,int,val, \
+ const struct timespec *,timeout,int *,uaddr2,int,val3)
+
static inline int host_to_target_sock_type(int host_type)
{
int target_type;
{
fd_set rfds, wfds, efds;
fd_set *rfds_ptr, *wfds_ptr, *efds_ptr;
- struct timeval tv, *tv_ptr;
+ struct timeval tv;
+ struct timespec ts, *ts_ptr;
abi_long ret;
ret = copy_from_user_fdset_ptr(&rfds, &rfds_ptr, rfd_addr, n);
if (target_tv_addr) {
if (copy_from_user_timeval(&tv, target_tv_addr))
return -TARGET_EFAULT;
- tv_ptr = &tv;
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+ ts_ptr = &ts;
} else {
- tv_ptr = NULL;
+ ts_ptr = NULL;
}
- ret = get_errno(select(n, rfds_ptr, wfds_ptr, efds_ptr, tv_ptr));
+ ret = get_errno(safe_pselect6(n, rfds_ptr, wfds_ptr, efds_ptr,
+ ts_ptr, NULL));
if (!is_error(ret)) {
if (rfd_addr && copy_to_user_fdset(rfd_addr, &rfds, n))
if (efd_addr && copy_to_user_fdset(efd_addr, &efds, n))
return -TARGET_EFAULT;
- if (target_tv_addr && copy_to_user_timeval(target_tv_addr, &tv))
- return -TARGET_EFAULT;
+ if (target_tv_addr) {
+ tv.tv_sec = ts.tv_sec;
+ tv.tv_usec = ts.tv_nsec / 1000;
+ if (copy_to_user_timeval(target_tv_addr, &tv)) {
+ return -TARGET_EFAULT;
+ }
+ }
}
return ret;
memcpy(addr, target_saddr, len);
addr->sa_family = sa_family;
- if (sa_family == AF_PACKET) {
+ if (sa_family == AF_NETLINK) {
+ struct sockaddr_nl *nladdr;
+
+ nladdr = (struct sockaddr_nl *)addr;
+ nladdr->nl_pid = tswap32(nladdr->nl_pid);
+ nladdr->nl_groups = tswap32(nladdr->nl_groups);
+ } else if (sa_family == AF_PACKET) {
struct target_sockaddr_ll *lladdr;
lladdr = (struct target_sockaddr_ll *)addr;
return -TARGET_EFAULT;
memcpy(target_saddr, addr, len);
target_saddr->sa_family = tswap16(addr->sa_family);
+ if (addr->sa_family == AF_NETLINK) {
+ struct sockaddr_nl *target_nl = (struct sockaddr_nl *)target_saddr;
+ target_nl->nl_pid = tswap32(target_nl->nl_pid);
+ target_nl->nl_groups = tswap32(target_nl->nl_groups);
+ }
unlock_user(target_saddr, target_addr, len);
return 0;
return 0;
}
+static void tswap_nlmsghdr(struct nlmsghdr *nlh)
+{
+ nlh->nlmsg_len = tswap32(nlh->nlmsg_len);
+ nlh->nlmsg_type = tswap16(nlh->nlmsg_type);
+ nlh->nlmsg_flags = tswap16(nlh->nlmsg_flags);
+ nlh->nlmsg_seq = tswap32(nlh->nlmsg_seq);
+ nlh->nlmsg_pid = tswap32(nlh->nlmsg_pid);
+}
+
+static abi_long host_to_target_for_each_nlmsg(struct nlmsghdr *nlh,
+ size_t len,
+ abi_long (*host_to_target_nlmsg)
+ (struct nlmsghdr *))
+{
+ uint32_t nlmsg_len;
+ abi_long ret;
+
+ while (len > sizeof(struct nlmsghdr)) {
+
+ nlmsg_len = nlh->nlmsg_len;
+ if (nlmsg_len < sizeof(struct nlmsghdr) ||
+ nlmsg_len > len) {
+ break;
+ }
+
+ switch (nlh->nlmsg_type) {
+ case NLMSG_DONE:
+ tswap_nlmsghdr(nlh);
+ return 0;
+ case NLMSG_NOOP:
+ break;
+ case NLMSG_ERROR:
+ {
+ struct nlmsgerr *e = NLMSG_DATA(nlh);
+ e->error = tswap32(e->error);
+ tswap_nlmsghdr(&e->msg);
+ tswap_nlmsghdr(nlh);
+ return 0;
+ }
+ default:
+ ret = host_to_target_nlmsg(nlh);
+ if (ret < 0) {
+ tswap_nlmsghdr(nlh);
+ return ret;
+ }
+ break;
+ }
+ tswap_nlmsghdr(nlh);
+ len -= NLMSG_ALIGN(nlmsg_len);
+ nlh = (struct nlmsghdr *)(((char*)nlh) + NLMSG_ALIGN(nlmsg_len));
+ }
+ return 0;
+}
+
+static abi_long target_to_host_for_each_nlmsg(struct nlmsghdr *nlh,
+ size_t len,
+ abi_long (*target_to_host_nlmsg)
+ (struct nlmsghdr *))
+{
+ int ret;
+
+ while (len > sizeof(struct nlmsghdr)) {
+ if (tswap32(nlh->nlmsg_len) < sizeof(struct nlmsghdr) ||
+ tswap32(nlh->nlmsg_len) > len) {
+ break;
+ }
+ tswap_nlmsghdr(nlh);
+ switch (nlh->nlmsg_type) {
+ case NLMSG_DONE:
+ return 0;
+ case NLMSG_NOOP:
+ break;
+ case NLMSG_ERROR:
+ {
+ struct nlmsgerr *e = NLMSG_DATA(nlh);
+ e->error = tswap32(e->error);
+ tswap_nlmsghdr(&e->msg);
+ }
+ default:
+ ret = target_to_host_nlmsg(nlh);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ len -= NLMSG_ALIGN(nlh->nlmsg_len);
+ nlh = (struct nlmsghdr *)(((char *)nlh) + NLMSG_ALIGN(nlh->nlmsg_len));
+ }
+ return 0;
+}
+
+static abi_long host_to_target_for_each_rtattr(struct rtattr *rtattr,
+ size_t len,
+ abi_long (*host_to_target_rtattr)
+ (struct rtattr *))
+{
+ unsigned short rta_len;
+ abi_long ret;
+
+ while (len > sizeof(struct rtattr)) {
+ rta_len = rtattr->rta_len;
+ if (rta_len < sizeof(struct rtattr) ||
+ rta_len > len) {
+ break;
+ }
+ ret = host_to_target_rtattr(rtattr);
+ rtattr->rta_len = tswap16(rtattr->rta_len);
+ rtattr->rta_type = tswap16(rtattr->rta_type);
+ if (ret < 0) {
+ return ret;
+ }
+ len -= RTA_ALIGN(rta_len);
+ rtattr = (struct rtattr *)(((char *)rtattr) + RTA_ALIGN(rta_len));
+ }
+ return 0;
+}
+
+static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr)
+{
+ uint32_t *u32;
+ struct rtnl_link_stats *st;
+ struct rtnl_link_stats64 *st64;
+ struct rtnl_link_ifmap *map;
+
+ switch (rtattr->rta_type) {
+ /* binary stream */
+ case IFLA_ADDRESS:
+ case IFLA_BROADCAST:
+ /* string */
+ case IFLA_IFNAME:
+ case IFLA_QDISC:
+ break;
+ /* uin8_t */
+ case IFLA_OPERSTATE:
+ case IFLA_LINKMODE:
+ case IFLA_CARRIER:
+ case IFLA_PROTO_DOWN:
+ break;
+ /* uint32_t */
+ case IFLA_MTU:
+ case IFLA_LINK:
+ case IFLA_WEIGHT:
+ case IFLA_TXQLEN:
+ case IFLA_CARRIER_CHANGES:
+ case IFLA_NUM_RX_QUEUES:
+ case IFLA_NUM_TX_QUEUES:
+ case IFLA_PROMISCUITY:
+ case IFLA_EXT_MASK:
+ case IFLA_LINK_NETNSID:
+ case IFLA_GROUP:
+ case IFLA_MASTER:
+ case IFLA_NUM_VF:
+ u32 = RTA_DATA(rtattr);
+ *u32 = tswap32(*u32);
+ break;
+ /* struct rtnl_link_stats */
+ case IFLA_STATS:
+ st = RTA_DATA(rtattr);
+ st->rx_packets = tswap32(st->rx_packets);
+ st->tx_packets = tswap32(st->tx_packets);
+ st->rx_bytes = tswap32(st->rx_bytes);
+ st->tx_bytes = tswap32(st->tx_bytes);
+ st->rx_errors = tswap32(st->rx_errors);
+ st->tx_errors = tswap32(st->tx_errors);
+ st->rx_dropped = tswap32(st->rx_dropped);
+ st->tx_dropped = tswap32(st->tx_dropped);
+ st->multicast = tswap32(st->multicast);
+ st->collisions = tswap32(st->collisions);
+
+ /* detailed rx_errors: */
+ st->rx_length_errors = tswap32(st->rx_length_errors);
+ st->rx_over_errors = tswap32(st->rx_over_errors);
+ st->rx_crc_errors = tswap32(st->rx_crc_errors);
+ st->rx_frame_errors = tswap32(st->rx_frame_errors);
+ st->rx_fifo_errors = tswap32(st->rx_fifo_errors);
+ st->rx_missed_errors = tswap32(st->rx_missed_errors);
+
+ /* detailed tx_errors */
+ st->tx_aborted_errors = tswap32(st->tx_aborted_errors);
+ st->tx_carrier_errors = tswap32(st->tx_carrier_errors);
+ st->tx_fifo_errors = tswap32(st->tx_fifo_errors);
+ st->tx_heartbeat_errors = tswap32(st->tx_heartbeat_errors);
+ st->tx_window_errors = tswap32(st->tx_window_errors);
+
+ /* for cslip etc */
+ st->rx_compressed = tswap32(st->rx_compressed);
+ st->tx_compressed = tswap32(st->tx_compressed);
+ break;
+ /* struct rtnl_link_stats64 */
+ case IFLA_STATS64:
+ st64 = RTA_DATA(rtattr);
+ st64->rx_packets = tswap64(st64->rx_packets);
+ st64->tx_packets = tswap64(st64->tx_packets);
+ st64->rx_bytes = tswap64(st64->rx_bytes);
+ st64->tx_bytes = tswap64(st64->tx_bytes);
+ st64->rx_errors = tswap64(st64->rx_errors);
+ st64->tx_errors = tswap64(st64->tx_errors);
+ st64->rx_dropped = tswap64(st64->rx_dropped);
+ st64->tx_dropped = tswap64(st64->tx_dropped);
+ st64->multicast = tswap64(st64->multicast);
+ st64->collisions = tswap64(st64->collisions);
+
+ /* detailed rx_errors: */
+ st64->rx_length_errors = tswap64(st64->rx_length_errors);
+ st64->rx_over_errors = tswap64(st64->rx_over_errors);
+ st64->rx_crc_errors = tswap64(st64->rx_crc_errors);
+ st64->rx_frame_errors = tswap64(st64->rx_frame_errors);
+ st64->rx_fifo_errors = tswap64(st64->rx_fifo_errors);
+ st64->rx_missed_errors = tswap64(st64->rx_missed_errors);
+
+ /* detailed tx_errors */
+ st64->tx_aborted_errors = tswap64(st64->tx_aborted_errors);
+ st64->tx_carrier_errors = tswap64(st64->tx_carrier_errors);
+ st64->tx_fifo_errors = tswap64(st64->tx_fifo_errors);
+ st64->tx_heartbeat_errors = tswap64(st64->tx_heartbeat_errors);
+ st64->tx_window_errors = tswap64(st64->tx_window_errors);
+
+ /* for cslip etc */
+ st64->rx_compressed = tswap64(st64->rx_compressed);
+ st64->tx_compressed = tswap64(st64->tx_compressed);
+ break;
+ /* struct rtnl_link_ifmap */
+ case IFLA_MAP:
+ map = RTA_DATA(rtattr);
+ map->mem_start = tswap64(map->mem_start);
+ map->mem_end = tswap64(map->mem_end);
+ map->base_addr = tswap64(map->base_addr);
+ map->irq = tswap16(map->irq);
+ break;
+ /* nested */
+ case IFLA_AF_SPEC:
+ case IFLA_LINKINFO:
+ /* FIXME: implement nested type */
+ gemu_log("Unimplemented nested type %d\n", rtattr->rta_type);
+ break;
+ default:
+ gemu_log("Unknown host IFLA type: %d\n", rtattr->rta_type);
+ break;
+ }
+ return 0;
+}
+
+static abi_long host_to_target_data_addr_rtattr(struct rtattr *rtattr)
+{
+ uint32_t *u32;
+ struct ifa_cacheinfo *ci;
+
+ switch (rtattr->rta_type) {
+ /* binary: depends on family type */
+ case IFA_ADDRESS:
+ case IFA_LOCAL:
+ break;
+ /* string */
+ case IFA_LABEL:
+ break;
+ /* u32 */
+ case IFA_FLAGS:
+ case IFA_BROADCAST:
+ u32 = RTA_DATA(rtattr);
+ *u32 = tswap32(*u32);
+ break;
+ /* struct ifa_cacheinfo */
+ case IFA_CACHEINFO:
+ ci = RTA_DATA(rtattr);
+ ci->ifa_prefered = tswap32(ci->ifa_prefered);
+ ci->ifa_valid = tswap32(ci->ifa_valid);
+ ci->cstamp = tswap32(ci->cstamp);
+ ci->tstamp = tswap32(ci->tstamp);
+ break;
+ default:
+ gemu_log("Unknown host IFA type: %d\n", rtattr->rta_type);
+ break;
+ }
+ return 0;
+}
+
+static abi_long host_to_target_data_route_rtattr(struct rtattr *rtattr)
+{
+ uint32_t *u32;
+ switch (rtattr->rta_type) {
+ /* binary: depends on family type */
+ case RTA_GATEWAY:
+ case RTA_DST:
+ case RTA_PREFSRC:
+ break;
+ /* u32 */
+ case RTA_PRIORITY:
+ case RTA_TABLE:
+ case RTA_OIF:
+ u32 = RTA_DATA(rtattr);
+ *u32 = tswap32(*u32);
+ break;
+ default:
+ gemu_log("Unknown host RTA type: %d\n", rtattr->rta_type);
+ break;
+ }
+ return 0;
+}
+
+static abi_long host_to_target_link_rtattr(struct rtattr *rtattr,
+ uint32_t rtattr_len)
+{
+ return host_to_target_for_each_rtattr(rtattr, rtattr_len,
+ host_to_target_data_link_rtattr);
+}
+
+static abi_long host_to_target_addr_rtattr(struct rtattr *rtattr,
+ uint32_t rtattr_len)
+{
+ return host_to_target_for_each_rtattr(rtattr, rtattr_len,
+ host_to_target_data_addr_rtattr);
+}
+
+static abi_long host_to_target_route_rtattr(struct rtattr *rtattr,
+ uint32_t rtattr_len)
+{
+ return host_to_target_for_each_rtattr(rtattr, rtattr_len,
+ host_to_target_data_route_rtattr);
+}
+
+static abi_long host_to_target_data_route(struct nlmsghdr *nlh)
+{
+ uint32_t nlmsg_len;
+ struct ifinfomsg *ifi;
+ struct ifaddrmsg *ifa;
+ struct rtmsg *rtm;
+
+ nlmsg_len = nlh->nlmsg_len;
+ switch (nlh->nlmsg_type) {
+ case RTM_NEWLINK:
+ case RTM_DELLINK:
+ case RTM_GETLINK:
+ ifi = NLMSG_DATA(nlh);
+ ifi->ifi_type = tswap16(ifi->ifi_type);
+ ifi->ifi_index = tswap32(ifi->ifi_index);
+ ifi->ifi_flags = tswap32(ifi->ifi_flags);
+ ifi->ifi_change = tswap32(ifi->ifi_change);
+ host_to_target_link_rtattr(IFLA_RTA(ifi),
+ nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)));
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ case RTM_GETADDR:
+ ifa = NLMSG_DATA(nlh);
+ ifa->ifa_index = tswap32(ifa->ifa_index);
+ host_to_target_addr_rtattr(IFA_RTA(ifa),
+ nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
+ break;
+ case RTM_NEWROUTE:
+ case RTM_DELROUTE:
+ case RTM_GETROUTE:
+ rtm = NLMSG_DATA(nlh);
+ rtm->rtm_flags = tswap32(rtm->rtm_flags);
+ host_to_target_route_rtattr(RTM_RTA(rtm),
+ nlmsg_len - NLMSG_LENGTH(sizeof(*rtm)));
+ break;
+ default:
+ return -TARGET_EINVAL;
+ }
+ return 0;
+}
+
+static inline abi_long host_to_target_nlmsg_route(struct nlmsghdr *nlh,
+ size_t len)
+{
+ return host_to_target_for_each_nlmsg(nlh, len, host_to_target_data_route);
+}
+
+static abi_long target_to_host_for_each_rtattr(struct rtattr *rtattr,
+ size_t len,
+ abi_long (*target_to_host_rtattr)
+ (struct rtattr *))
+{
+ abi_long ret;
+
+ while (len >= sizeof(struct rtattr)) {
+ if (tswap16(rtattr->rta_len) < sizeof(struct rtattr) ||
+ tswap16(rtattr->rta_len) > len) {
+ break;
+ }
+ rtattr->rta_len = tswap16(rtattr->rta_len);
+ rtattr->rta_type = tswap16(rtattr->rta_type);
+ ret = target_to_host_rtattr(rtattr);
+ if (ret < 0) {
+ return ret;
+ }
+ len -= RTA_ALIGN(rtattr->rta_len);
+ rtattr = (struct rtattr *)(((char *)rtattr) +
+ RTA_ALIGN(rtattr->rta_len));
+ }
+ return 0;
+}
+
+static abi_long target_to_host_data_link_rtattr(struct rtattr *rtattr)
+{
+ switch (rtattr->rta_type) {
+ default:
+ gemu_log("Unknown target IFLA type: %d\n", rtattr->rta_type);
+ break;
+ }
+ return 0;
+}
+
+static abi_long target_to_host_data_addr_rtattr(struct rtattr *rtattr)
+{
+ switch (rtattr->rta_type) {
+ /* binary: depends on family type */
+ case IFA_LOCAL:
+ case IFA_ADDRESS:
+ break;
+ default:
+ gemu_log("Unknown target IFA type: %d\n", rtattr->rta_type);
+ break;
+ }
+ return 0;
+}
+
+static abi_long target_to_host_data_route_rtattr(struct rtattr *rtattr)
+{
+ uint32_t *u32;
+ switch (rtattr->rta_type) {
+ /* binary: depends on family type */
+ case RTA_DST:
+ case RTA_SRC:
+ case RTA_GATEWAY:
+ break;
+ /* u32 */
+ case RTA_OIF:
+ u32 = RTA_DATA(rtattr);
+ *u32 = tswap32(*u32);
+ break;
+ default:
+ gemu_log("Unknown target RTA type: %d\n", rtattr->rta_type);
+ break;
+ }
+ return 0;
+}
+
+static void target_to_host_link_rtattr(struct rtattr *rtattr,
+ uint32_t rtattr_len)
+{
+ target_to_host_for_each_rtattr(rtattr, rtattr_len,
+ target_to_host_data_link_rtattr);
+}
+
+static void target_to_host_addr_rtattr(struct rtattr *rtattr,
+ uint32_t rtattr_len)
+{
+ target_to_host_for_each_rtattr(rtattr, rtattr_len,
+ target_to_host_data_addr_rtattr);
+}
+
+static void target_to_host_route_rtattr(struct rtattr *rtattr,
+ uint32_t rtattr_len)
+{
+ target_to_host_for_each_rtattr(rtattr, rtattr_len,
+ target_to_host_data_route_rtattr);
+}
+
+static abi_long target_to_host_data_route(struct nlmsghdr *nlh)
+{
+ struct ifinfomsg *ifi;
+ struct ifaddrmsg *ifa;
+ struct rtmsg *rtm;
+
+ switch (nlh->nlmsg_type) {
+ case RTM_GETLINK:
+ break;
+ case RTM_NEWLINK:
+ case RTM_DELLINK:
+ ifi = NLMSG_DATA(nlh);
+ ifi->ifi_type = tswap16(ifi->ifi_type);
+ ifi->ifi_index = tswap32(ifi->ifi_index);
+ ifi->ifi_flags = tswap32(ifi->ifi_flags);
+ ifi->ifi_change = tswap32(ifi->ifi_change);
+ target_to_host_link_rtattr(IFLA_RTA(ifi), nlh->nlmsg_len -
+ NLMSG_LENGTH(sizeof(*ifi)));
+ break;
+ case RTM_GETADDR:
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifa = NLMSG_DATA(nlh);
+ ifa->ifa_index = tswap32(ifa->ifa_index);
+ target_to_host_addr_rtattr(IFA_RTA(ifa), nlh->nlmsg_len -
+ NLMSG_LENGTH(sizeof(*ifa)));
+ break;
+ case RTM_GETROUTE:
+ break;
+ case RTM_NEWROUTE:
+ case RTM_DELROUTE:
+ rtm = NLMSG_DATA(nlh);
+ rtm->rtm_flags = tswap32(rtm->rtm_flags);
+ target_to_host_route_rtattr(RTM_RTA(rtm), nlh->nlmsg_len -
+ NLMSG_LENGTH(sizeof(*rtm)));
+ break;
+ default:
+ return -TARGET_EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static abi_long target_to_host_nlmsg_route(struct nlmsghdr *nlh, size_t len)
+{
+ return target_to_host_for_each_nlmsg(nlh, len, target_to_host_data_route);
+}
+
+static abi_long host_to_target_data_audit(struct nlmsghdr *nlh)
+{
+ switch (nlh->nlmsg_type) {
+ default:
+ gemu_log("Unknown host audit message type %d\n",
+ nlh->nlmsg_type);
+ return -TARGET_EINVAL;
+ }
+ return 0;
+}
+
+static inline abi_long host_to_target_nlmsg_audit(struct nlmsghdr *nlh,
+ size_t len)
+{
+ return host_to_target_for_each_nlmsg(nlh, len, host_to_target_data_audit);
+}
+
+static abi_long target_to_host_data_audit(struct nlmsghdr *nlh)
+{
+ switch (nlh->nlmsg_type) {
+ case AUDIT_USER:
+ case AUDIT_FIRST_USER_MSG ... AUDIT_LAST_USER_MSG:
+ case AUDIT_FIRST_USER_MSG2 ... AUDIT_LAST_USER_MSG2:
+ break;
+ default:
+ gemu_log("Unknown target audit message type %d\n",
+ nlh->nlmsg_type);
+ return -TARGET_EINVAL;
+ }
+
+ return 0;
+}
+
+static abi_long target_to_host_nlmsg_audit(struct nlmsghdr *nlh, size_t len)
+{
+ return target_to_host_for_each_nlmsg(nlh, len, target_to_host_data_audit);
+}
+
/* do_setsockopt() Must return target values and target errnos. */
static abi_long do_setsockopt(int sockfd, int level, int optname,
abi_ulong optval_addr, socklen_t optlen)
.target_to_host_addr = packet_target_to_host_sockaddr,
};
+static abi_long netlink_route_target_to_host(void *buf, size_t len)
+{
+ return target_to_host_nlmsg_route(buf, len);
+}
+
+static abi_long netlink_route_host_to_target(void *buf, size_t len)
+{
+ return host_to_target_nlmsg_route(buf, len);
+}
+
+static TargetFdTrans target_netlink_route_trans = {
+ .target_to_host_data = netlink_route_target_to_host,
+ .host_to_target_data = netlink_route_host_to_target,
+};
+
+static abi_long netlink_audit_target_to_host(void *buf, size_t len)
+{
+ return target_to_host_nlmsg_audit(buf, len);
+}
+
+static abi_long netlink_audit_host_to_target(void *buf, size_t len)
+{
+ return host_to_target_nlmsg_audit(buf, len);
+}
+
+static TargetFdTrans target_netlink_audit_trans = {
+ .target_to_host_data = netlink_audit_target_to_host,
+ .host_to_target_data = netlink_audit_host_to_target,
+};
+
/* do_socket() Must return target values and target errnos. */
static abi_long do_socket(int domain, int type, int protocol)
{
return ret;
}
- if (domain == PF_NETLINK)
- return -TARGET_EAFNOSUPPORT;
+ if (domain == PF_NETLINK &&
+ !(protocol == NETLINK_ROUTE ||
+ protocol == NETLINK_KOBJECT_UEVENT ||
+ protocol == NETLINK_AUDIT)) {
+ return -EPFNOSUPPORT;
+ }
if (domain == AF_PACKET ||
(domain == AF_INET && type == SOCK_PACKET)) {
* if socket type is SOCK_PACKET, bind by name
*/
fd_trans_register(ret, &target_packet_trans);
+ } else if (domain == PF_NETLINK) {
+ switch (protocol) {
+ case NETLINK_ROUTE:
+ fd_trans_register(ret, &target_netlink_route_trans);
+ break;
+ case NETLINK_KOBJECT_UEVENT:
+ /* nothing to do: messages are strings */
+ break;
+ case NETLINK_AUDIT:
+ fd_trans_register(ret, &target_netlink_audit_trans);
+ break;
+ default:
+ g_assert_not_reached();
+ }
}
}
return ret;
msg.msg_iov = vec;
if (send) {
- ret = target_to_host_cmsg(&msg, msgp);
- if (ret == 0)
+ if (fd_trans_target_to_host_data(fd)) {
+ ret = fd_trans_target_to_host_data(fd)(msg.msg_iov->iov_base,
+ msg.msg_iov->iov_len);
+ } else {
+ ret = target_to_host_cmsg(&msg, msgp);
+ }
+ if (ret == 0) {
ret = get_errno(sendmsg(fd, &msg, flags));
+ }
} else {
ret = get_errno(recvmsg(fd, &msg, flags));
if (!is_error(ret)) {
len = ret;
- ret = host_to_target_cmsg(msgp, &msg);
+ if (fd_trans_host_to_target_data(fd)) {
+ ret = fd_trans_host_to_target_data(fd)(msg.msg_iov->iov_base,
+ msg.msg_iov->iov_len);
+ } else {
+ ret = host_to_target_cmsg(msgp, &msg);
+ }
if (!is_error(ret)) {
msgp->msg_namelen = tswap32(msg.msg_namelen);
if (msg.msg_name != NULL) {
host_msg = lock_user(VERIFY_READ, msg, len, 1);
if (!host_msg)
return -TARGET_EFAULT;
+ if (fd_trans_target_to_host_data(fd)) {
+ ret = fd_trans_target_to_host_data(fd)(host_msg, len);
+ if (ret < 0) {
+ unlock_user(host_msg, msg, 0);
+ return ret;
+ }
+ }
if (target_addr) {
addr = alloca(addrlen+1);
ret = target_to_host_sockaddr(fd, addr, target_addr, addrlen);
}
static inline abi_long do_msgrcv(int msqid, abi_long msgp,
- unsigned int msgsz, abi_long msgtyp,
+ ssize_t msgsz, abi_long msgtyp,
int msgflg)
{
struct target_msgbuf *target_mb;
struct msgbuf *host_mb;
abi_long ret = 0;
+ if (msgsz < 0) {
+ return -TARGET_EINVAL;
+ }
+
if (!lock_user_struct(VERIFY_WRITE, target_mb, msgp, 0))
return -TARGET_EFAULT;
- host_mb = g_malloc(msgsz+sizeof(long));
+ host_mb = g_try_malloc(msgsz + sizeof(long));
+ if (!host_mb) {
+ ret = -TARGET_ENOMEM;
+ goto end;
+ }
ret = get_errno(msgrcv(msqid, host_mb, msgsz, msgtyp, msgflg));
if (ret > 0) {
#endif /* USE_UID16 */
+/* We must do direct syscalls for setting UID/GID, because we want to
+ * implement the Linux system call semantics of "change only for this thread",
+ * not the libc/POSIX semantics of "change for all threads in process".
+ * (See http://ewontfix.com/17/ for more details.)
+ * We use the 32-bit version of the syscalls if present; if it is not
+ * then either the host architecture supports 32-bit UIDs natively with
+ * the standard syscall, or the 16-bit UID is the best we can do.
+ */
+#ifdef __NR_setuid32
+#define __NR_sys_setuid __NR_setuid32
+#else
+#define __NR_sys_setuid __NR_setuid
+#endif
+#ifdef __NR_setgid32
+#define __NR_sys_setgid __NR_setgid32
+#else
+#define __NR_sys_setgid __NR_setgid
+#endif
+#ifdef __NR_setresuid32
+#define __NR_sys_setresuid __NR_setresuid32
+#else
+#define __NR_sys_setresuid __NR_setresuid
+#endif
+#ifdef __NR_setresgid32
+#define __NR_sys_setresgid __NR_setresgid32
+#else
+#define __NR_sys_setresgid __NR_setresgid
+#endif
+
+_syscall1(int, sys_setuid, uid_t, uid)
+_syscall1(int, sys_setgid, gid_t, gid)
+_syscall3(int, sys_setresuid, uid_t, ruid, uid_t, euid, uid_t, suid)
+_syscall3(int, sys_setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid)
+
void syscall_init(void)
{
IOCTLEntry *ie;
if (!lock_user_struct(VERIFY_READ, target_ts, target_addr, 1))
return -TARGET_EFAULT;
- host_ts->tv_sec = tswapal(target_ts->tv_sec);
- host_ts->tv_nsec = tswapal(target_ts->tv_nsec);
+ __get_user(host_ts->tv_sec, &target_ts->tv_sec);
+ __get_user(host_ts->tv_nsec, &target_ts->tv_nsec);
unlock_user_struct(target_ts, target_addr, 0);
return 0;
}
if (!lock_user_struct(VERIFY_WRITE, target_ts, target_addr, 0))
return -TARGET_EFAULT;
- target_ts->tv_sec = tswapal(host_ts->tv_sec);
- target_ts->tv_nsec = tswapal(host_ts->tv_nsec);
+ __put_user(host_ts->tv_sec, &target_ts->tv_sec);
+ __put_user(host_ts->tv_nsec, &target_ts->tv_nsec);
unlock_user_struct(target_ts, target_addr, 1);
return 0;
}
}
#endif
-#if defined(TARGET_NR_stat64) || defined(TARGET_NR_newfstatat)
static inline abi_long host_to_target_stat64(void *cpu_env,
abi_ulong target_addr,
struct stat *host_st)
return 0;
}
-#endif
/* ??? Using host futex calls even when target atomic operations
are not really atomic probably breaks things. However implementing
} else {
pts = NULL;
}
- return get_errno(sys_futex(g2h(uaddr), op, tswap32(val),
+ return get_errno(safe_futex(g2h(uaddr), op, tswap32(val),
pts, NULL, val3));
case FUTEX_WAKE:
- return get_errno(sys_futex(g2h(uaddr), op, val, NULL, NULL, 0));
+ return get_errno(safe_futex(g2h(uaddr), op, val, NULL, NULL, 0));
case FUTEX_FD:
- return get_errno(sys_futex(g2h(uaddr), op, val, NULL, NULL, 0));
+ return get_errno(safe_futex(g2h(uaddr), op, val, NULL, NULL, 0));
case FUTEX_REQUEUE:
case FUTEX_CMP_REQUEUE:
case FUTEX_WAKE_OP:
to satisfy the compiler. We do not need to tswap TIMEOUT
since it's not compared to guest memory. */
pts = (struct timespec *)(uintptr_t) timeout;
- return get_errno(sys_futex(g2h(uaddr), op, val, pts,
- g2h(uaddr2),
- (base_op == FUTEX_CMP_REQUEUE
- ? tswap32(val3)
- : val3)));
+ return get_errno(safe_futex(g2h(uaddr), op, val, pts,
+ g2h(uaddr2),
+ (base_op == FUTEX_CMP_REQUEUE
+ ? tswap32(val3)
+ : val3)));
default:
return -TARGET_ENOSYS;
}
nb_read = read(fd_orig, buf, sizeof(buf));
if (nb_read < 0) {
+ int e = errno;
fd_orig = close(fd_orig);
+ errno = e;
return -1;
} else if (nb_read == 0) {
break;
if (word_skipped) {
if (write(fd, cp_buf, nb_read) != nb_read) {
+ int e = errno;
close(fd_orig);
+ errno = e;
return -1;
}
}
fp = fopen("/proc/self/maps", "r");
if (fp == NULL) {
- return -EACCES;
+ return -1;
}
while ((read = getline(&line, &len, fp)) != -1) {
fp = fopen("/proc/net/route", "r");
if (fp == NULL) {
- return -EACCES;
+ return -1;
}
/* read header */
if (is_proc_myself(pathname, "exe")) {
int execfd = qemu_getauxval(AT_EXECFD);
- return execfd ? execfd : get_errno(sys_openat(dirfd, exec_path, flags, mode));
+ return execfd ? execfd : safe_openat(dirfd, exec_path, flags, mode);
}
for (fake_open = fakes; fake_open->filename; fake_open++) {
unlink(filename);
if ((r = fake_open->fill(cpu_env, fd))) {
+ int e = errno;
close(fd);
+ errno = e;
return r;
}
lseek(fd, 0, SEEK_SET);
return fd;
}
- return get_errno(sys_openat(dirfd, path(pathname), flags, mode));
+ return safe_openat(dirfd, path(pathname), flags, mode);
}
#define TIMER_MAGIC 0x0caf0000
struct statfs stfs;
void *p;
+#if defined(DEBUG_ERESTARTSYS)
+ /* Debug-only code for exercising the syscall-restart code paths
+ * in the per-architecture cpu main loops: restart every syscall
+ * the guest makes once before letting it through.
+ */
+ {
+ static int flag;
+
+ flag = !flag;
+ if (flag) {
+ return -TARGET_ERESTARTSYS;
+ }
+ }
+#endif
+
#ifdef DEBUG
gemu_log("syscall %d", num);
#endif
else {
if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
goto efault;
- ret = get_errno(read(arg1, p, arg3));
+ ret = get_errno(safe_read(arg1, p, arg3));
if (ret >= 0 &&
fd_trans_host_to_target_data(arg1)) {
ret = fd_trans_host_to_target_data(arg1)(p, ret);
case TARGET_NR_write:
if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
goto efault;
- ret = get_errno(write(arg1, p, arg3));
+ ret = get_errno(safe_write(arg1, p, arg3));
unlock_user(p, arg2, 0);
break;
#ifdef TARGET_NR_open
case TARGET_NR_waitpid:
{
int status;
- ret = get_errno(waitpid(arg1, &status, arg3));
+ ret = get_errno(safe_wait4(arg1, &status, arg3, 0));
if (!is_error(ret) && arg2 && ret
&& put_user_s32(host_to_target_waitstatus(status), arg2))
goto efault;
{
siginfo_t info;
info.si_pid = 0;
- ret = get_errno(waitid(arg1, arg2, &info, arg4));
+ ret = get_errno(safe_waitid(arg1, arg2, &info, arg4, NULL));
if (!is_error(ret) && arg3 && info.si_pid != 0) {
if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_siginfo_t), 0)))
goto efault;
if (!(p = lock_user_string(arg1)))
goto execve_efault;
- ret = get_errno(execve(p, argp, envp));
+ /* Although execve() is not an interruptible syscall it is
+ * a special case where we must use the safe_syscall wrapper:
+ * if we allow a signal to happen before we make the host
+ * syscall then we will 'lose' it, because at the point of
+ * execve the process leaves QEMU's control. So we use the
+ * safe syscall wrapper to ensure that we either take the
+ * signal as a guest signal, or else it does not happen
+ * before the execve completes and makes it the other
+ * program's problem.
+ */
+ ret = get_errno(safe_execve(p, argp, envp));
unlock_user(p, arg1, 0);
goto execve_end;
break;
#ifdef TARGET_NR_sigreturn
case TARGET_NR_sigreturn:
- /* NOTE: ret is eax, so not transcoding must be done */
ret = do_sigreturn(cpu_env);
break;
#endif
case TARGET_NR_rt_sigreturn:
- /* NOTE: ret is eax, so not transcoding must be done */
ret = do_rt_sigreturn(cpu_env);
break;
case TARGET_NR_sethostname:
sig_ptr = NULL;
}
- ret = get_errno(sys_pselect6(n, rfds_ptr, wfds_ptr, efds_ptr,
- ts_ptr, sig_ptr));
+ ret = get_errno(safe_pselect6(n, rfds_ptr, wfds_ptr, efds_ptr,
+ ts_ptr, sig_ptr));
if (!is_error(ret)) {
if (rfd_addr && copy_to_user_fdset(rfd_addr, &rfds, n))
ret = get_errno(shutdown(arg1, arg2));
break;
#endif
+#if defined(TARGET_NR_getrandom) && defined(__NR_getrandom)
+ case TARGET_NR_getrandom:
+ p = lock_user(VERIFY_WRITE, arg1, arg2, 0);
+ if (!p) {
+ goto efault;
+ }
+ ret = get_errno(getrandom(p, arg2, arg3));
+ unlock_user(p, arg1, ret);
+ break;
+#endif
#ifdef TARGET_NR_socket
case TARGET_NR_socket:
ret = do_socket(arg1, arg2, arg3);
rusage_ptr = &rusage;
else
rusage_ptr = NULL;
- ret = get_errno(wait4(arg1, &status, arg3, rusage_ptr));
+ ret = get_errno(safe_wait4(arg1, &status, arg3, rusage_ptr));
if (!is_error(ret)) {
if (status_ptr && ret) {
status = host_to_target_waitstatus(status);
#endif
#ifdef TARGET_NR_setresuid
case TARGET_NR_setresuid:
- ret = get_errno(setresuid(low2highuid(arg1),
- low2highuid(arg2),
- low2highuid(arg3)));
+ ret = get_errno(sys_setresuid(low2highuid(arg1),
+ low2highuid(arg2),
+ low2highuid(arg3)));
break;
#endif
#ifdef TARGET_NR_getresuid
#endif
#ifdef TARGET_NR_getresgid
case TARGET_NR_setresgid:
- ret = get_errno(setresgid(low2highgid(arg1),
- low2highgid(arg2),
- low2highgid(arg3)));
+ ret = get_errno(sys_setresgid(low2highgid(arg1),
+ low2highgid(arg2),
+ low2highgid(arg3)));
break;
#endif
#ifdef TARGET_NR_getresgid
break;
#endif
case TARGET_NR_setuid:
- ret = get_errno(setuid(low2highuid(arg1)));
+ ret = get_errno(sys_setuid(low2highuid(arg1)));
break;
case TARGET_NR_setgid:
- ret = get_errno(setgid(low2highgid(arg1)));
+ ret = get_errno(sys_setgid(low2highgid(arg1)));
break;
case TARGET_NR_setfsuid:
ret = get_errno(setfsuid(arg1));
#endif
#ifdef TARGET_NR_setresuid32
case TARGET_NR_setresuid32:
- ret = get_errno(setresuid(arg1, arg2, arg3));
+ ret = get_errno(sys_setresuid(arg1, arg2, arg3));
break;
#endif
#ifdef TARGET_NR_getresuid32
#endif
#ifdef TARGET_NR_setresgid32
case TARGET_NR_setresgid32:
- ret = get_errno(setresgid(arg1, arg2, arg3));
+ ret = get_errno(sys_setresgid(arg1, arg2, arg3));
break;
#endif
#ifdef TARGET_NR_getresgid32
#endif
#ifdef TARGET_NR_setuid32
case TARGET_NR_setuid32:
- ret = get_errno(setuid(arg1));
+ ret = get_errno(sys_setuid(arg1));
break;
#endif
#ifdef TARGET_NR_setgid32
case TARGET_NR_setgid32:
- ret = get_errno(setgid(arg1));
+ ret = get_errno(sys_setgid(arg1));
break;
#endif
#ifdef TARGET_NR_setfsuid32