summary refs log tree commit diff stats
path: root/linux-user/syscall.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux-user/syscall.c')
-rw-r--r--linux-user/syscall.c454
1 files changed, 435 insertions, 19 deletions
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 8bf6205dc2..ca6a2b495a 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -100,9 +100,11 @@ int __clone2(int (*fn)(void *), void *child_stack_base,
 #include <linux/route.h>
 #include <linux/filter.h>
 #include <linux/blkpg.h>
+#include <netpacket/packet.h>
 #include <linux/netlink.h>
 #ifdef CONFIG_RTNETLINK
 #include <linux/rtnetlink.h>
+#include <linux/if_bridge.h>
 #endif
 #include <linux/audit.h>
 #include "linux_loop.h"
@@ -1374,15 +1376,26 @@ static inline abi_long host_to_target_sockaddr(abi_ulong target_addr,
 {
     struct target_sockaddr *target_saddr;
 
+    if (len == 0) {
+        return 0;
+    }
+
     target_saddr = lock_user(VERIFY_WRITE, target_addr, len, 0);
     if (!target_saddr)
         return -TARGET_EFAULT;
     memcpy(target_saddr, addr, len);
-    target_saddr->sa_family = tswap16(addr->sa_family);
-    if (addr->sa_family == AF_NETLINK) {
+    if (len >= offsetof(struct target_sockaddr, sa_family) +
+        sizeof(target_saddr->sa_family)) {
+        target_saddr->sa_family = tswap16(addr->sa_family);
+    }
+    if (addr->sa_family == AF_NETLINK && len >= sizeof(struct sockaddr_nl)) {
         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);
+    } else if (addr->sa_family == AF_PACKET) {
+        struct sockaddr_ll *target_ll = (struct sockaddr_ll *)target_saddr;
+        target_ll->sll_ifindex = tswap32(target_ll->sll_ifindex);
+        target_ll->sll_hatype = tswap16(target_ll->sll_hatype);
     }
     unlock_user(target_saddr, target_addr, len);
 
@@ -1707,6 +1720,33 @@ static abi_long target_to_host_for_each_nlmsg(struct nlmsghdr *nlh,
 }
 
 #ifdef CONFIG_RTNETLINK
+static abi_long host_to_target_for_each_nlattr(struct nlattr *nlattr,
+                                               size_t len, void *context,
+                                               abi_long (*host_to_target_nlattr)
+                                                        (struct nlattr *,
+                                                         void *context))
+{
+    unsigned short nla_len;
+    abi_long ret;
+
+    while (len > sizeof(struct nlattr)) {
+        nla_len = nlattr->nla_len;
+        if (nla_len < sizeof(struct nlattr) ||
+            nla_len > len) {
+            break;
+        }
+        ret = host_to_target_nlattr(nlattr, context);
+        nlattr->nla_len = tswap16(nlattr->nla_len);
+        nlattr->nla_type = tswap16(nlattr->nla_type);
+        if (ret < 0) {
+            return ret;
+        }
+        len -= NLA_ALIGN(nla_len);
+        nlattr = (struct nlattr *)(((char *)nlattr) + NLA_ALIGN(nla_len));
+    }
+    return 0;
+}
+
 static abi_long host_to_target_for_each_rtattr(struct rtattr *rtattr,
                                                size_t len,
                                                abi_long (*host_to_target_rtattr)
@@ -1733,12 +1773,292 @@ static abi_long host_to_target_for_each_rtattr(struct rtattr *rtattr,
     return 0;
 }
 
+#define NLA_DATA(nla) ((void *)((char *)(nla)) + NLA_HDRLEN)
+
+static abi_long host_to_target_data_bridge_nlattr(struct nlattr *nlattr,
+                                                  void *context)
+{
+    uint16_t *u16;
+    uint32_t *u32;
+    uint64_t *u64;
+
+    switch (nlattr->nla_type) {
+    /* no data */
+    case IFLA_BR_FDB_FLUSH:
+        break;
+    /* binary */
+    case IFLA_BR_GROUP_ADDR:
+        break;
+    /* uint8_t */
+    case IFLA_BR_VLAN_FILTERING:
+    case IFLA_BR_TOPOLOGY_CHANGE:
+    case IFLA_BR_TOPOLOGY_CHANGE_DETECTED:
+    case IFLA_BR_MCAST_ROUTER:
+    case IFLA_BR_MCAST_SNOOPING:
+    case IFLA_BR_MCAST_QUERY_USE_IFADDR:
+    case IFLA_BR_MCAST_QUERIER:
+    case IFLA_BR_NF_CALL_IPTABLES:
+    case IFLA_BR_NF_CALL_IP6TABLES:
+    case IFLA_BR_NF_CALL_ARPTABLES:
+        break;
+    /* uint16_t */
+    case IFLA_BR_PRIORITY:
+    case IFLA_BR_VLAN_PROTOCOL:
+    case IFLA_BR_GROUP_FWD_MASK:
+    case IFLA_BR_ROOT_PORT:
+    case IFLA_BR_VLAN_DEFAULT_PVID:
+        u16 = NLA_DATA(nlattr);
+        *u16 = tswap16(*u16);
+        break;
+    /* uint32_t */
+    case IFLA_BR_FORWARD_DELAY:
+    case IFLA_BR_HELLO_TIME:
+    case IFLA_BR_MAX_AGE:
+    case IFLA_BR_AGEING_TIME:
+    case IFLA_BR_STP_STATE:
+    case IFLA_BR_ROOT_PATH_COST:
+    case IFLA_BR_MCAST_HASH_ELASTICITY:
+    case IFLA_BR_MCAST_HASH_MAX:
+    case IFLA_BR_MCAST_LAST_MEMBER_CNT:
+    case IFLA_BR_MCAST_STARTUP_QUERY_CNT:
+        u32 = NLA_DATA(nlattr);
+        *u32 = tswap32(*u32);
+        break;
+    /* uint64_t */
+    case IFLA_BR_HELLO_TIMER:
+    case IFLA_BR_TCN_TIMER:
+    case IFLA_BR_GC_TIMER:
+    case IFLA_BR_TOPOLOGY_CHANGE_TIMER:
+    case IFLA_BR_MCAST_LAST_MEMBER_INTVL:
+    case IFLA_BR_MCAST_MEMBERSHIP_INTVL:
+    case IFLA_BR_MCAST_QUERIER_INTVL:
+    case IFLA_BR_MCAST_QUERY_INTVL:
+    case IFLA_BR_MCAST_QUERY_RESPONSE_INTVL:
+    case IFLA_BR_MCAST_STARTUP_QUERY_INTVL:
+        u64 = NLA_DATA(nlattr);
+        *u64 = tswap64(*u64);
+        break;
+    /* ifla_bridge_id: uin8_t[] */
+    case IFLA_BR_ROOT_ID:
+    case IFLA_BR_BRIDGE_ID:
+        break;
+    default:
+        gemu_log("Unknown IFLA_BR type %d\n", nlattr->nla_type);
+        break;
+    }
+    return 0;
+}
+
+static abi_long host_to_target_slave_data_bridge_nlattr(struct nlattr *nlattr,
+                                                        void *context)
+{
+    uint16_t *u16;
+    uint32_t *u32;
+    uint64_t *u64;
+
+    switch (nlattr->nla_type) {
+    /* uint8_t */
+    case IFLA_BRPORT_STATE:
+    case IFLA_BRPORT_MODE:
+    case IFLA_BRPORT_GUARD:
+    case IFLA_BRPORT_PROTECT:
+    case IFLA_BRPORT_FAST_LEAVE:
+    case IFLA_BRPORT_LEARNING:
+    case IFLA_BRPORT_UNICAST_FLOOD:
+    case IFLA_BRPORT_PROXYARP:
+    case IFLA_BRPORT_LEARNING_SYNC:
+    case IFLA_BRPORT_PROXYARP_WIFI:
+    case IFLA_BRPORT_TOPOLOGY_CHANGE_ACK:
+    case IFLA_BRPORT_CONFIG_PENDING:
+    case IFLA_BRPORT_MULTICAST_ROUTER:
+        break;
+    /* uint16_t */
+    case IFLA_BRPORT_PRIORITY:
+    case IFLA_BRPORT_DESIGNATED_PORT:
+    case IFLA_BRPORT_DESIGNATED_COST:
+    case IFLA_BRPORT_ID:
+    case IFLA_BRPORT_NO:
+        u16 = NLA_DATA(nlattr);
+        *u16 = tswap16(*u16);
+        break;
+    /* uin32_t */
+    case IFLA_BRPORT_COST:
+        u32 = NLA_DATA(nlattr);
+        *u32 = tswap32(*u32);
+        break;
+    /* uint64_t */
+    case IFLA_BRPORT_MESSAGE_AGE_TIMER:
+    case IFLA_BRPORT_FORWARD_DELAY_TIMER:
+    case IFLA_BRPORT_HOLD_TIMER:
+        u64 = NLA_DATA(nlattr);
+        *u64 = tswap64(*u64);
+        break;
+    /* ifla_bridge_id: uint8_t[] */
+    case IFLA_BRPORT_ROOT_ID:
+    case IFLA_BRPORT_BRIDGE_ID:
+        break;
+    default:
+        gemu_log("Unknown IFLA_BRPORT type %d\n", nlattr->nla_type);
+        break;
+    }
+    return 0;
+}
+
+struct linkinfo_context {
+    int len;
+    char *name;
+    int slave_len;
+    char *slave_name;
+};
+
+static abi_long host_to_target_data_linkinfo_nlattr(struct nlattr *nlattr,
+                                                    void *context)
+{
+    struct linkinfo_context *li_context = context;
+
+    switch (nlattr->nla_type) {
+    /* string */
+    case IFLA_INFO_KIND:
+        li_context->name = NLA_DATA(nlattr);
+        li_context->len = nlattr->nla_len - NLA_HDRLEN;
+        break;
+    case IFLA_INFO_SLAVE_KIND:
+        li_context->slave_name = NLA_DATA(nlattr);
+        li_context->slave_len = nlattr->nla_len - NLA_HDRLEN;
+        break;
+    /* stats */
+    case IFLA_INFO_XSTATS:
+        /* FIXME: only used by CAN */
+        break;
+    /* nested */
+    case IFLA_INFO_DATA:
+        if (strncmp(li_context->name, "bridge",
+                    li_context->len) == 0) {
+            return host_to_target_for_each_nlattr(NLA_DATA(nlattr),
+                                                  nlattr->nla_len,
+                                                  NULL,
+                                             host_to_target_data_bridge_nlattr);
+        } else {
+            gemu_log("Unknown IFLA_INFO_KIND %s\n", li_context->name);
+        }
+        break;
+    case IFLA_INFO_SLAVE_DATA:
+        if (strncmp(li_context->slave_name, "bridge",
+                    li_context->slave_len) == 0) {
+            return host_to_target_for_each_nlattr(NLA_DATA(nlattr),
+                                                  nlattr->nla_len,
+                                                  NULL,
+                                       host_to_target_slave_data_bridge_nlattr);
+        } else {
+            gemu_log("Unknown IFLA_INFO_SLAVE_KIND %s\n",
+                     li_context->slave_name);
+        }
+        break;
+    default:
+        gemu_log("Unknown host IFLA_INFO type: %d\n", nlattr->nla_type);
+        break;
+    }
+
+    return 0;
+}
+
+static abi_long host_to_target_data_inet_nlattr(struct nlattr *nlattr,
+                                                void *context)
+{
+    uint32_t *u32;
+    int i;
+
+    switch (nlattr->nla_type) {
+    case IFLA_INET_CONF:
+        u32 = NLA_DATA(nlattr);
+        for (i = 0; i < (nlattr->nla_len - NLA_HDRLEN) / sizeof(*u32);
+             i++) {
+            u32[i] = tswap32(u32[i]);
+        }
+        break;
+    default:
+        gemu_log("Unknown host AF_INET type: %d\n", nlattr->nla_type);
+    }
+    return 0;
+}
+
+static abi_long host_to_target_data_inet6_nlattr(struct nlattr *nlattr,
+                                                void *context)
+{
+    uint32_t *u32;
+    uint64_t *u64;
+    struct ifla_cacheinfo *ci;
+    int i;
+
+    switch (nlattr->nla_type) {
+    /* binaries */
+    case IFLA_INET6_TOKEN:
+        break;
+    /* uint8_t */
+    case IFLA_INET6_ADDR_GEN_MODE:
+        break;
+    /* uint32_t */
+    case IFLA_INET6_FLAGS:
+        u32 = NLA_DATA(nlattr);
+        *u32 = tswap32(*u32);
+        break;
+    /* uint32_t[] */
+    case IFLA_INET6_CONF:
+        u32 = NLA_DATA(nlattr);
+        for (i = 0; i < (nlattr->nla_len - NLA_HDRLEN) / sizeof(*u32);
+             i++) {
+            u32[i] = tswap32(u32[i]);
+        }
+        break;
+    /* ifla_cacheinfo */
+    case IFLA_INET6_CACHEINFO:
+        ci = NLA_DATA(nlattr);
+        ci->max_reasm_len = tswap32(ci->max_reasm_len);
+        ci->tstamp = tswap32(ci->tstamp);
+        ci->reachable_time = tswap32(ci->reachable_time);
+        ci->retrans_time = tswap32(ci->retrans_time);
+        break;
+    /* uint64_t[] */
+    case IFLA_INET6_STATS:
+    case IFLA_INET6_ICMP6STATS:
+        u64 = NLA_DATA(nlattr);
+        for (i = 0; i < (nlattr->nla_len - NLA_HDRLEN) / sizeof(*u64);
+             i++) {
+            u64[i] = tswap64(u64[i]);
+        }
+        break;
+    default:
+        gemu_log("Unknown host AF_INET6 type: %d\n", nlattr->nla_type);
+    }
+    return 0;
+}
+
+static abi_long host_to_target_data_spec_nlattr(struct nlattr *nlattr,
+                                                    void *context)
+{
+    switch (nlattr->nla_type) {
+    case AF_INET:
+        return host_to_target_for_each_nlattr(NLA_DATA(nlattr), nlattr->nla_len,
+                                              NULL,
+                                             host_to_target_data_inet_nlattr);
+    case AF_INET6:
+        return host_to_target_for_each_nlattr(NLA_DATA(nlattr), nlattr->nla_len,
+                                              NULL,
+                                             host_to_target_data_inet6_nlattr);
+    default:
+        gemu_log("Unknown host AF_SPEC type: %d\n", nlattr->nla_type);
+        break;
+    }
+    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;
+    struct linkinfo_context li_context;
 
     switch (rtattr->rta_type) {
     /* binary stream */
@@ -1846,11 +2166,15 @@ static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr)
         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;
+        memset(&li_context, 0, sizeof(li_context));
+        return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len,
+                                              &li_context,
+                                           host_to_target_data_linkinfo_nlattr);
+    case IFLA_AF_SPEC:
+        return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len,
+                                              NULL,
+                                             host_to_target_data_spec_nlattr);
     default:
         gemu_log("Unknown host IFLA type: %d\n", rtattr->rta_type);
         break;
@@ -2826,12 +3150,26 @@ static TargetFdTrans target_packet_trans = {
 #ifdef CONFIG_RTNETLINK
 static abi_long netlink_route_target_to_host(void *buf, size_t len)
 {
-    return target_to_host_nlmsg_route(buf, len);
+    abi_long ret;
+
+    ret = target_to_host_nlmsg_route(buf, len);
+    if (ret < 0) {
+        return ret;
+    }
+
+    return len;
 }
 
 static abi_long netlink_route_host_to_target(void *buf, size_t len)
 {
-    return host_to_target_nlmsg_route(buf, len);
+    abi_long ret;
+
+    ret = host_to_target_nlmsg_route(buf, len);
+    if (ret < 0) {
+        return ret;
+    }
+
+    return len;
 }
 
 static TargetFdTrans target_netlink_route_trans = {
@@ -2842,12 +3180,26 @@ static TargetFdTrans target_netlink_route_trans = {
 
 static abi_long netlink_audit_target_to_host(void *buf, size_t len)
 {
-    return target_to_host_nlmsg_audit(buf, len);
+    abi_long ret;
+
+    ret = target_to_host_nlmsg_audit(buf, len);
+    if (ret < 0) {
+        return ret;
+    }
+
+    return len;
 }
 
 static abi_long netlink_audit_host_to_target(void *buf, size_t len)
 {
-    return host_to_target_nlmsg_audit(buf, len);
+    abi_long ret;
+
+    ret = host_to_target_nlmsg_audit(buf, len);
+    if (ret < 0) {
+        return ret;
+    }
+
+    return len;
 }
 
 static TargetFdTrans target_netlink_audit_trans = {
@@ -2989,13 +3341,22 @@ static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp,
 
     if (send) {
         if (fd_trans_target_to_host_data(fd)) {
-            ret = fd_trans_target_to_host_data(fd)(msg.msg_iov->iov_base,
+            void *host_msg;
+
+            host_msg = g_malloc(msg.msg_iov->iov_len);
+            memcpy(host_msg, msg.msg_iov->iov_base, msg.msg_iov->iov_len);
+            ret = fd_trans_target_to_host_data(fd)(host_msg,
                                                    msg.msg_iov->iov_len);
+            if (ret >= 0) {
+                msg.msg_iov->iov_base = host_msg;
+                ret = get_errno(safe_sendmsg(fd, &msg, flags));
+            }
+            g_free(host_msg);
         } else {
             ret = target_to_host_cmsg(&msg, msgp);
-        }
-        if (ret == 0) {
-            ret = get_errno(safe_sendmsg(fd, &msg, flags));
+            if (ret == 0) {
+                ret = get_errno(safe_sendmsg(fd, &msg, flags));
+            }
         }
     } else {
         ret = get_errno(safe_recvmsg(fd, &msg, flags));
@@ -3211,6 +3572,7 @@ static abi_long do_sendto(int fd, abi_ulong msg, size_t len, int flags,
 {
     void *addr;
     void *host_msg;
+    void *copy_msg = NULL;
     abi_long ret;
 
     if ((int)addrlen < 0) {
@@ -3221,23 +3583,29 @@ static abi_long do_sendto(int fd, abi_ulong msg, size_t len, int flags,
     if (!host_msg)
         return -TARGET_EFAULT;
     if (fd_trans_target_to_host_data(fd)) {
+        copy_msg = host_msg;
+        host_msg = g_malloc(len);
+        memcpy(host_msg, copy_msg, len);
         ret = fd_trans_target_to_host_data(fd)(host_msg, len);
         if (ret < 0) {
-            unlock_user(host_msg, msg, 0);
-            return ret;
+            goto fail;
         }
     }
     if (target_addr) {
         addr = alloca(addrlen+1);
         ret = target_to_host_sockaddr(fd, addr, target_addr, addrlen);
         if (ret) {
-            unlock_user(host_msg, msg, 0);
-            return ret;
+            goto fail;
         }
         ret = get_errno(safe_sendto(fd, host_msg, len, flags, addr, addrlen));
     } else {
         ret = get_errno(safe_sendto(fd, host_msg, len, flags, NULL, 0));
     }
+fail:
+    if (copy_msg) {
+        g_free(host_msg);
+        host_msg = copy_msg;
+    }
     unlock_user(host_msg, msg, 0);
     return ret;
 }
@@ -3272,6 +3640,9 @@ static abi_long do_recvfrom(int fd, abi_ulong msg, size_t len, int flags,
         ret = get_errno(safe_recvfrom(fd, host_msg, len, flags, NULL, 0));
     }
     if (!is_error(ret)) {
+        if (fd_trans_host_to_target_data(fd)) {
+            ret = fd_trans_host_to_target_data(fd)(host_msg, ret);
+        }
         if (target_addr) {
             host_to_target_sockaddr(target_addr, addr, addrlen);
             if (put_user_u32(addrlen, target_addrlen)) {
@@ -7614,7 +7985,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
 #if defined(TARGET_ALPHA)
             struct target_sigaction act, oact, *pact = 0;
             struct target_rt_sigaction *rt_act;
-            /* ??? arg4 == sizeof(sigset_t).  */
+
+            if (arg4 != sizeof(target_sigset_t)) {
+                ret = -TARGET_EINVAL;
+                break;
+            }
             if (arg2) {
                 if (!lock_user_struct(VERIFY_READ, rt_act, arg2, 1))
                     goto efault;
@@ -7638,6 +8013,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
             struct target_sigaction *act;
             struct target_sigaction *oact;
 
+            if (arg4 != sizeof(target_sigset_t)) {
+                ret = -TARGET_EINVAL;
+                break;
+            }
             if (arg2) {
                 if (!lock_user_struct(VERIFY_READ, act, arg2, 1))
                     goto efault;
@@ -7769,6 +8148,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
             int how = arg1;
             sigset_t set, oldset, *set_ptr;
 
+            if (arg4 != sizeof(target_sigset_t)) {
+                ret = -TARGET_EINVAL;
+                break;
+            }
+
             if (arg2) {
                 switch(how) {
                 case TARGET_SIG_BLOCK:
@@ -7819,6 +8203,17 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
     case TARGET_NR_rt_sigpending:
         {
             sigset_t set;
+
+            /* Yes, this check is >, not != like most. We follow the kernel's
+             * logic and it does it like this because it implements
+             * NR_sigpending through the same code path, and in that case
+             * the old_sigset_t is smaller in size.
+             */
+            if (arg2 > sizeof(target_sigset_t)) {
+                ret = -TARGET_EINVAL;
+                break;
+            }
+
             ret = get_errno(sigpending(&set));
             if (!is_error(ret)) {
                 if (!(p = lock_user(VERIFY_WRITE, arg1, sizeof(target_sigset_t), 0)))
@@ -7852,6 +8247,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
     case TARGET_NR_rt_sigsuspend:
         {
             TaskState *ts = cpu->opaque;
+
+            if (arg2 != sizeof(target_sigset_t)) {
+                ret = -TARGET_EINVAL;
+                break;
+            }
             if (!(p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1)))
                 goto efault;
             target_to_host_sigset(&ts->sigsuspend_mask, p);
@@ -7869,6 +8269,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
             struct timespec uts, *puts;
             siginfo_t uinfo;
 
+            if (arg4 != sizeof(target_sigset_t)) {
+                ret = -TARGET_EINVAL;
+                break;
+            }
+
             if (!(p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1)))
                 goto efault;
             target_to_host_sigset(&set, p);
@@ -9120,6 +9525,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
                 }
 
                 if (arg4) {
+                    if (arg5 != sizeof(target_sigset_t)) {
+                        unlock_user(target_pfd, arg1, 0);
+                        ret = -TARGET_EINVAL;
+                        break;
+                    }
+
                     target_set = lock_user(VERIFY_READ, arg4, sizeof(target_sigset_t), 1);
                     if (!target_set) {
                         unlock_user(target_pfd, arg1, 0);
@@ -10939,6 +11350,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
             sigset_t _set, *set = &_set;
 
             if (arg5) {
+                if (arg6 != sizeof(target_sigset_t)) {
+                    ret = -TARGET_EINVAL;
+                    break;
+                }
+
                 target_set = lock_user(VERIFY_READ, arg5,
                                        sizeof(target_sigset_t), 1);
                 if (!target_set) {