Le 09/01/2018 à 21:16, Samuel Thibault a écrit :
> 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 <samuel.thiba...@ens-lyon.org>
> Reviewed-by: Laurent Vivier <laur...@vivier.eu>
> 
> ---
> Difference from v1: bitmask computation was separated out into
> target_to_host_cpu_mask()/host_to_target_cpu_mask().
> 
>  linux-user/syscall.c | 81 
> ++++++++++++++++++++++++++++++++++++++++++++++------
>  1 file changed, 73 insertions(+), 8 deletions(-)
> 
> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
> index 11c9116c4a..cac07419aa 100644
> --- a/linux-user/syscall.c
> +++ b/linux-user/syscall.c
> @@ -7716,6 +7716,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_<errcode>. */
> @@ -10353,6 +10420,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)) {
> @@ -10372,9 +10440,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;
> @@ -10392,13 +10458,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));
>          }
> 

Applied to my linux-user branch.

Thanks,
Laurent

Reply via email to