xtensa: fix {get,put}_user() for 64bit values
authorAl Viro <viro@zeniv.linux.org.uk>
Wed, 9 Oct 2019 19:21:05 +0000 (20:21 +0100)
committerMax Filippov <jcmvbkbc@gmail.com>
Mon, 14 Oct 2019 18:39:50 +0000 (11:39 -0700)
First of all, on short copies __copy_{to,from}_user() return the amount
of bytes left uncopied, *not* -EFAULT.  get_user() and put_user() are
expected to return -EFAULT on failure.

Another problem is get_user(v32, (__u64 __user *)p); that should
fetch 64bit value and the assign it to v32, truncating it in process.
Current code, OTOH, reads 8 bytes of data and stores them at the
address of v32, stomping on the 4 bytes that follow v32 itself.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
arch/xtensa/include/asm/uaccess.h

index 6792928ba84a7bb16d20a661f5070d0676e2f6b6..f568c00392ece5735009ec8d94ba5d016c879873 100644 (file)
@@ -100,7 +100,7 @@ do {                                                                        \
        case 4: __put_user_asm(x, ptr, retval, 4, "s32i", __cb); break; \
        case 8: {                                                       \
                     __typeof__(*ptr) __v64 = x;                        \
-                    retval = __copy_to_user(ptr, &__v64, 8);           \
+                    retval = __copy_to_user(ptr, &__v64, 8) ? -EFAULT : 0;     \
                     break;                                             \
                }                                                       \
        default: __put_user_bad();                                      \
@@ -198,7 +198,16 @@ do {                                                                       \
        case 1: __get_user_asm(x, ptr, retval, 1, "l8ui", __cb);  break;\
        case 2: __get_user_asm(x, ptr, retval, 2, "l16ui", __cb); break;\
        case 4: __get_user_asm(x, ptr, retval, 4, "l32i", __cb);  break;\
-       case 8: retval = __copy_from_user(&x, ptr, 8);    break;        \
+       case 8: {                                                       \
+               u64 __x;                                                \
+               if (unlikely(__copy_from_user(&__x, ptr, 8))) {         \
+                       retval = -EFAULT;                               \
+                       (x) = 0;                                        \
+               } else {                                                \
+                       (x) = *(__force __typeof__((ptr)))&__x;         \
+               }                                                       \
+               break;                                                  \
+       }                                                               \
        default: (x) = __get_user_bad();                                \
        }                                                               \
 } while (0)