OSDN Git Service

Merge tag 'v4.9.95' into android-4.9.95
[android-x86/kernel.git] / arch / arm64 / include / asm / uaccess.h
index 95cbade..0d71949 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/thread_info.h>
 
 #include <asm/cpufeature.h>
+#include <asm/processor.h>
 #include <asm/ptrace.h>
 #include <asm/errno.h>
 #include <asm/memory.h>
@@ -64,10 +65,7 @@ struct exception_table_entry
 
 extern int fixup_exception(struct pt_regs *regs);
 
-#define KERNEL_DS      (-1UL)
 #define get_ds()       (KERNEL_DS)
-
-#define USER_DS                TASK_SIZE_64
 #define get_fs()       (current_thread_info()->addr_limit)
 
 static inline void set_fs(mm_segment_t fs)
@@ -75,6 +73,13 @@ static inline void set_fs(mm_segment_t fs)
        current_thread_info()->addr_limit = fs;
 
        /*
+        * Prevent a mispredicted conditional call to set_fs from forwarding
+        * the wrong address limit to access_ok under speculation.
+        */
+       dsb(nsh);
+       isb();
+
+       /*
         * Enable/disable UAO so that copy_to_user() etc can access
         * kernel memory with the unprivileged instructions.
         */
@@ -92,22 +97,32 @@ static inline void set_fs(mm_segment_t fs)
  * Returns 1 if the range is valid, 0 otherwise.
  *
  * This is equivalent to the following test:
- * (u65)addr + (u65)size <= current->addr_limit
- *
- * This needs 65-bit arithmetic.
+ * (u65)addr + (u65)size <= (u65)current->addr_limit + 1
  */
-#define __range_ok(addr, size)                                         \
-({                                                                     \
-       unsigned long __addr = (unsigned long __force)(addr);           \
-       unsigned long flag, roksum;                                     \
-       __chk_user_ptr(addr);                                           \
-       asm("adds %1, %1, %3; ccmp %1, %4, #2, cc; cset %0, ls"         \
-               : "=&r" (flag), "=&r" (roksum)                          \
-               : "1" (__addr), "Ir" (size),                            \
-                 "r" (current_thread_info()->addr_limit)               \
-               : "cc");                                                \
-       flag;                                                           \
-})
+static inline unsigned long __range_ok(unsigned long addr, unsigned long size)
+{
+       unsigned long limit = current_thread_info()->addr_limit;
+
+       __chk_user_ptr(addr);
+       asm volatile(
+       // A + B <= C + 1 for all A,B,C, in four easy steps:
+       // 1: X = A + B; X' = X % 2^64
+       "       adds    %0, %0, %2\n"
+       // 2: Set C = 0 if X > 2^64, to guarantee X' > C in step 4
+       "       csel    %1, xzr, %1, hi\n"
+       // 3: Set X' = ~0 if X >= 2^64. For X == 2^64, this decrements X'
+       //    to compensate for the carry flag being set in step 4. For
+       //    X > 2^64, X' merely has to remain nonzero, which it does.
+       "       csinv   %0, %0, xzr, cc\n"
+       // 4: For X < 2^64, this gives us X' - C - 1 <= 0, where the -1
+       //    comes from the carry in being clear. Otherwise, we are
+       //    testing X' - C == 0, subject to the previous adjustments.
+       "       sbcs    xzr, %0, %1\n"
+       "       cset    %0, ls\n"
+       : "+r" (addr), "+r" (limit) : "Ir" (size) : "cc");
+
+       return addr;
+}
 
 /*
  * When dealing with data aborts, watchpoints, or instruction traps we may end
@@ -116,7 +131,7 @@ static inline void set_fs(mm_segment_t fs)
  */
 #define untagged_addr(addr)            sign_extend64(addr, 55)
 
-#define access_ok(type, addr, size)    __range_ok(addr, size)
+#define access_ok(type, addr, size)    __range_ok((unsigned long)(addr), size)
 #define user_addr_max                  get_fs
 
 #define _ASM_EXTABLE(from, to)                                         \
@@ -235,6 +250,26 @@ static inline void uaccess_enable_not_uao(void)
 }
 
 /*
+ * Sanitise a uaccess pointer such that it becomes NULL if above the
+ * current addr_limit.
+ */
+#define uaccess_mask_ptr(ptr) (__typeof__(ptr))__uaccess_mask_ptr(ptr)
+static inline void __user *__uaccess_mask_ptr(const void __user *ptr)
+{
+       void __user *safe_ptr;
+
+       asm volatile(
+       "       bics    xzr, %1, %2\n"
+       "       csel    %0, %1, xzr, eq\n"
+       : "=&r" (safe_ptr)
+       : "r" (ptr), "r" (current_thread_info()->addr_limit)
+       : "cc");
+
+       csdb();
+       return safe_ptr;
+}
+
+/*
  * The "__xxx" versions of the user access functions do not verify the address
  * space - it must have been done previously with a separate "access_ok()"
  * call.
@@ -286,30 +321,35 @@ do {                                                                      \
        (x) = (__force __typeof__(*(ptr)))__gu_val;                     \
 } while (0)
 
-#define __get_user(x, ptr)                                             \
+#define __get_user_check(x, ptr, err)                                  \
 ({                                                                     \
-       int __gu_err = 0;                                               \
-       __get_user_err((x), (ptr), __gu_err);                           \
-       __gu_err;                                                       \
+       __typeof__(*(ptr)) __user *__p = (ptr);                         \
+       might_fault();                                                  \
+       if (access_ok(VERIFY_READ, __p, sizeof(*__p))) {                \
+               __p = uaccess_mask_ptr(__p);                            \
+               __get_user_err((x), __p, (err));                        \
+       } else {                                                        \
+               (x) = 0; (err) = -EFAULT;                               \
+       }                                                               \
 })
 
 #define __get_user_error(x, ptr, err)                                  \
 ({                                                                     \
-       __get_user_err((x), (ptr), (err));                              \
+       __get_user_check((x), (ptr), (err));                            \
        (void)0;                                                        \
 })
 
-#define __get_user_unaligned __get_user
-
-#define get_user(x, ptr)                                               \
+#define __get_user(x, ptr)                                             \
 ({                                                                     \
-       __typeof__(*(ptr)) __user *__p = (ptr);                         \
-       might_fault();                                                  \
-       access_ok(VERIFY_READ, __p, sizeof(*__p)) ?                     \
-               __get_user((x), __p) :                                  \
-               ((x) = 0, -EFAULT);                                     \
+       int __gu_err = 0;                                               \
+       __get_user_check((x), (ptr), __gu_err);                         \
+       __gu_err;                                                       \
 })
 
+#define __get_user_unaligned __get_user
+
+#define get_user       __get_user
+
 #define __put_user_asm(instr, alt_instr, reg, x, addr, err, feature)   \
        asm volatile(                                                   \
        "1:"ALTERNATIVE(instr "     " reg "1, [%2]\n",                  \
@@ -352,47 +392,51 @@ do {                                                                      \
        uaccess_disable_not_uao();                                      \
 } while (0)
 
-#define __put_user(x, ptr)                                             \
+#define __put_user_check(x, ptr, err)                                  \
 ({                                                                     \
-       int __pu_err = 0;                                               \
-       __put_user_err((x), (ptr), __pu_err);                           \
-       __pu_err;                                                       \
+       __typeof__(*(ptr)) __user *__p = (ptr);                         \
+       might_fault();                                                  \
+       if (access_ok(VERIFY_WRITE, __p, sizeof(*__p))) {               \
+               __p = uaccess_mask_ptr(__p);                            \
+               __put_user_err((x), __p, (err));                        \
+       } else  {                                                       \
+               (err) = -EFAULT;                                        \
+       }                                                               \
 })
 
 #define __put_user_error(x, ptr, err)                                  \
 ({                                                                     \
-       __put_user_err((x), (ptr), (err));                              \
+       __put_user_check((x), (ptr), (err));                            \
        (void)0;                                                        \
 })
 
-#define __put_user_unaligned __put_user
-
-#define put_user(x, ptr)                                               \
+#define __put_user(x, ptr)                                             \
 ({                                                                     \
-       __typeof__(*(ptr)) __user *__p = (ptr);                         \
-       might_fault();                                                  \
-       access_ok(VERIFY_WRITE, __p, sizeof(*__p)) ?                    \
-               __put_user((x), __p) :                                  \
-               -EFAULT;                                                \
+       int __pu_err = 0;                                               \
+       __put_user_check((x), (ptr), __pu_err);                         \
+       __pu_err;                                                       \
 })
 
+#define __put_user_unaligned __put_user
+
+#define put_user       __put_user
+
 extern unsigned long __must_check __arch_copy_from_user(void *to, const void __user *from, unsigned long n);
 extern unsigned long __must_check __arch_copy_to_user(void __user *to, const void *from, unsigned long n);
-extern unsigned long __must_check __copy_in_user(void __user *to, const void __user *from, unsigned long n);
-extern unsigned long __must_check __clear_user(void __user *addr, unsigned long n);
+extern unsigned long __must_check __arch_copy_in_user(void __user *to, const void __user *from, unsigned long n);
 
 static inline unsigned long __must_check __copy_from_user(void *to, const void __user *from, unsigned long n)
 {
        kasan_check_write(to, n);
        check_object_size(to, n, false);
-       return __arch_copy_from_user(to, from, n);
+       return __arch_copy_from_user(to, __uaccess_mask_ptr(from), n);
 }
 
 static inline unsigned long __must_check __copy_to_user(void __user *to, const void *from, unsigned long n)
 {
        kasan_check_read(from, n);
        check_object_size(from, n, true);
-       return __arch_copy_to_user(to, from, n);
+       return __arch_copy_to_user(__uaccess_mask_ptr(to), from, n);
 }
 
 static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
@@ -420,22 +464,25 @@ static inline unsigned long __must_check copy_to_user(void __user *to, const voi
        return n;
 }
 
-static inline unsigned long __must_check copy_in_user(void __user *to, const void __user *from, unsigned long n)
+static inline unsigned long __must_check __copy_in_user(void __user *to, const void __user *from, unsigned long n)
 {
        if (access_ok(VERIFY_READ, from, n) && access_ok(VERIFY_WRITE, to, n))
-               n = __copy_in_user(to, from, n);
+               n = __arch_copy_in_user(__uaccess_mask_ptr(to), __uaccess_mask_ptr(from), n);
        return n;
 }
+#define copy_in_user __copy_in_user
 
 #define __copy_to_user_inatomic __copy_to_user
 #define __copy_from_user_inatomic __copy_from_user
 
-static inline unsigned long __must_check clear_user(void __user *to, unsigned long n)
+extern unsigned long __must_check __arch_clear_user(void __user *to, unsigned long n);
+static inline unsigned long __must_check __clear_user(void __user *to, unsigned long n)
 {
        if (access_ok(VERIFY_WRITE, to, n))
-               n = __clear_user(to, n);
+               n = __arch_clear_user(__uaccess_mask_ptr(to), n);
        return n;
 }
+#define clear_user     __clear_user
 
 extern long strncpy_from_user(char *dest, const char __user *src, long count);