s390/uaccess: fix __put_get_user_asm define
authorHeiko Carstens <heiko.carstens@de.ibm.com>
Mon, 20 Jun 2016 08:35:20 +0000 (10:35 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Tue, 28 Jun 2016 07:32:27 +0000 (09:32 +0200)
The __put_get_user_asm defines an inline assmembly which makes use of
the asm register construct. The parameters passed to that define may
also contain function calls.

It is a gcc restriction that between register asm statements and the
use of any such annotated variables function calls may clobber the
register / variable contents. Or in other words: gcc would generate
broken code.

This can be achieved e.g. with the following code:

    get_user(x, func() ? a : b);

where the call of func would clobber register zero which is used by
the __put_get_user_asm define.
To avoid this add two static inline functions which don't have these
side effects.

Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/uaccess.h

index 2ea7f3208cd2cd9ed2ba207fa6a04085be6911ff..9b49cf1daa8f9ee11d3536343fe76325085efa64 100644 (file)
@@ -151,8 +151,65 @@ unsigned long __must_check __copy_to_user(void __user *to, const void *from,
        __rc;                                                   \
 })
 
-#define __put_user_fn(x, ptr, size) __put_get_user_asm(ptr, x, size, 0x810000UL)
-#define __get_user_fn(x, ptr, size) __put_get_user_asm(x, ptr, size, 0x81UL)
+static inline int __put_user_fn(void *x, void __user *ptr, unsigned long size)
+{
+       unsigned long spec = 0x810000UL;
+       int rc;
+
+       switch (size) {
+       case 1:
+               rc = __put_get_user_asm((unsigned char __user *)ptr,
+                                       (unsigned char *)x,
+                                       size, spec);
+               break;
+       case 2:
+               rc = __put_get_user_asm((unsigned short __user *)ptr,
+                                       (unsigned short *)x,
+                                       size, spec);
+               break;
+       case 4:
+               rc = __put_get_user_asm((unsigned int __user *)ptr,
+                                       (unsigned int *)x,
+                                       size, spec);
+               break;
+       case 8:
+               rc = __put_get_user_asm((unsigned long __user *)ptr,
+                                       (unsigned long *)x,
+                                       size, spec);
+               break;
+       };
+       return rc;
+}
+
+static inline int __get_user_fn(void *x, const void __user *ptr, unsigned long size)
+{
+       unsigned long spec = 0x81UL;
+       int rc;
+
+       switch (size) {
+       case 1:
+               rc = __put_get_user_asm((unsigned char *)x,
+                                       (unsigned char __user *)ptr,
+                                       size, spec);
+               break;
+       case 2:
+               rc = __put_get_user_asm((unsigned short *)x,
+                                       (unsigned short __user *)ptr,
+                                       size, spec);
+               break;
+       case 4:
+               rc = __put_get_user_asm((unsigned int *)x,
+                                       (unsigned int __user *)ptr,
+                                       size, spec);
+               break;
+       case 8:
+               rc = __put_get_user_asm((unsigned long *)x,
+                                       (unsigned long __user *)ptr,
+                                       size, spec);
+               break;
+       };
+       return rc;
+}
 
 #else /* CONFIG_HAVE_MARCH_Z10_FEATURES */