]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ARM: uaccess: Implement missing __get_user_asm_dword()
authorThomas Gleixner <tglx@linutronix.de>
Mon, 27 Oct 2025 08:43:42 +0000 (09:43 +0100)
committerPeter Zijlstra <peterz@infradead.org>
Mon, 3 Nov 2025 14:26:09 +0000 (15:26 +0100)
When CONFIG_CPU_SPECTRE=n then get_user() is missing the 8 byte ASM variant
for no real good reason. This prevents using get_user(u64) in generic code.

Implement it as a sequence of two 4-byte reads with LE/BE awareness and
make the unsigned long (or long long) type for the intermediate variable to
read into dependend on the the target type.

The __long_type() macro and idea was lifted from PowerPC. Thanks to
Christophe for pointing it out.

Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202509120155.pFgwfeUD-lkp@intel.com/
Link: https://patch.msgid.link/20251027083745.168468637@linutronix.de
arch/arm/include/asm/uaccess.h

index f90be312418e87b53ab99d6059617c121b85825e..d6ae80b5df36f65f5f289472f5cfd5532d2aca8f 100644 (file)
@@ -283,10 +283,17 @@ extern int __put_user_8(void *, unsigned long long);
        __gu_err;                                                       \
 })
 
+/*
+ * This is a type: either unsigned long, if the argument fits into
+ * that type, or otherwise unsigned long long.
+ */
+#define __long_type(x) \
+       __typeof__(__builtin_choose_expr(sizeof(x) > sizeof(0UL), 0ULL, 0UL))
+
 #define __get_user_err(x, ptr, err, __t)                               \
 do {                                                                   \
        unsigned long __gu_addr = (unsigned long)(ptr);                 \
-       unsigned long __gu_val;                                         \
+       __long_type(x) __gu_val;                                        \
        unsigned int __ua_flags;                                        \
        __chk_user_ptr(ptr);                                            \
        might_fault();                                                  \
@@ -295,6 +302,7 @@ do {                                                                        \
        case 1: __get_user_asm_byte(__gu_val, __gu_addr, err, __t); break;      \
        case 2: __get_user_asm_half(__gu_val, __gu_addr, err, __t); break;      \
        case 4: __get_user_asm_word(__gu_val, __gu_addr, err, __t); break;      \
+       case 8: __get_user_asm_dword(__gu_val, __gu_addr, err, __t); break;     \
        default: (__gu_val) = __get_user_bad();                         \
        }                                                               \
        uaccess_restore(__ua_flags);                                    \
@@ -353,6 +361,22 @@ do {                                                                       \
 #define __get_user_asm_word(x, addr, err, __t)                 \
        __get_user_asm(x, addr, err, "ldr" __t)
 
+#ifdef __ARMEB__
+#define __WORD0_OFFS   4
+#define __WORD1_OFFS   0
+#else
+#define __WORD0_OFFS   0
+#define __WORD1_OFFS   4
+#endif
+
+#define __get_user_asm_dword(x, addr, err, __t)                                \
+       ({                                                              \
+       unsigned long __w0, __w1;                                       \
+       __get_user_asm(__w0, addr + __WORD0_OFFS, err, "ldr" __t);      \
+       __get_user_asm(__w1, addr + __WORD1_OFFS, err, "ldr" __t);      \
+       (x) = ((u64)__w1 << 32) | (u64) __w0;                           \
+})
+
 #define __put_user_switch(x, ptr, __err, __fn)                         \
        do {                                                            \
                const __typeof__(*(ptr)) __user *__pu_ptr = (ptr);      \