diff mbox

[PULL,07/13] linux-user: Fix sched_get/setaffinity conversion

Message ID 20180123144807.5618-8-laurent@vivier.eu (mailing list archive)
State New, archived
Headers show

Commit Message

Laurent Vivier Jan. 23, 2018, 2:48 p.m. UTC
From: Samuel Thibault <samuel.thibault@ens-lyon.org>

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.thibault@ens-lyon.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
Message-Id: <20180109201643.1479-1-samuel.thibault@ens-lyon.org>
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 linux-user/syscall.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 73 insertions(+), 8 deletions(-)

Comments

Peter Maydell Jan. 26, 2018, 6:23 p.m. UTC | #1
On 23 January 2018 at 14:48, Laurent Vivier <laurent@vivier.eu> wrote:
> From: Samuel Thibault <samuel.thibault@ens-lyon.org>
>
> 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.thibault@ens-lyon.org>
> Reviewed-by: Laurent Vivier <laurent@vivier.eu>
> Message-Id: <20180109201643.1479-1-samuel.thibault@ens-lyon.org>
> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
> ---

> @@ -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;

Hi -- Coverity spots that in this change, we now have a case
where we set "ret = arg2;" which then immediately is replaced
by "ret = host_to_target_cpu_mask(mask, mask_size, arg3, arg2);",
making the first assignment pointless.

It looks like we're now ignoring the host filled buffer size
that is returned by sys_sched_getaffinity() and then adjusted
by this bit of code. Shouldn't we be using that value in this
new host_to_target_cpu_mask() code?

thanks
-- PMM
Samuel Thibault Jan. 26, 2018, 6:33 p.m. UTC | #2
Peter Maydell, on ven. 26 janv. 2018 18:23:02 +0000, wrote:
> On 23 January 2018 at 14:48, Laurent Vivier <laurent@vivier.eu> wrote:
> > From: Samuel Thibault <samuel.thibault@ens-lyon.org>
> >
> > 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.thibault@ens-lyon.org>
> > Reviewed-by: Laurent Vivier <laurent@vivier.eu>
> > Message-Id: <20180109201643.1479-1-samuel.thibault@ens-lyon.org>
> > Signed-off-by: Laurent Vivier <laurent@vivier.eu>
> > ---
> 
> > @@ -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;
> 
> Hi -- Coverity spots that in this change, we now have a case
> where we set "ret = arg2;" which then immediately is replaced
> by "ret = host_to_target_cpu_mask(mask, mask_size, arg3, arg2);",
> making the first assignment pointless.
> 
> It looks like we're now ignoring the host filled buffer size
> that is returned by sys_sched_getaffinity() and then adjusted
> by this bit of code. Shouldn't we be using that value in this
> new host_to_target_cpu_mask() code?

Indeed, will send a patch against this.

Samuel
diff mbox

Patch

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_<errcode>. */
@@ -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));
         }