[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <87y0qd89q9.ffs@tglx>
Date: Wed, 17 Sep 2025 15:55:10 +0200
From: Thomas Gleixner <tglx@...utronix.de>
To: "Russell King (Oracle)" <linux@...linux.org.uk>
Cc: LKML <linux-kernel@...r.kernel.org>, Linus Torvalds
<torvalds@...ux-foundation.org>, Peter Zijlstra <peterz@...radead.org>,
kernel test robot <lkp@...el.com>, linux-arm-kernel@...ts.infradead.org,
Nathan Chancellor <nathan@...nel.org>, Christophe Leroy
<christophe.leroy@...roup.eu>, Darren Hart <dvhart@...radead.org>,
Davidlohr Bueso <dave@...olabs.net>, André Almeida
<andrealmeid@...lia.com>, x86@...nel.org, Alexander Viro
<viro@...iv.linux.org.uk>, Christian Brauner <brauner@...nel.org>, Jan
Kara <jack@...e.cz>, linux-fsdevel@...r.kernel.org
Subject: Re: [patch V2 1/6] ARM: uaccess: Implement missing
__get_user_asm_dword()
On Wed, Sep 17 2025 at 10:41, Russell King wrote:
> On Wed, Sep 17, 2025 at 07:48:00AM +0200, Thomas Gleixner wrote:
>
> Putting together a simple test case, where the only change is making
> __gu_val an unsigned long long:
>
> t.c: In function ‘get_ptr’:
> t.c:40:15: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
> 40 | (x) = (__typeof__(*(ptr)))__gu_val; \
> | ^
> t.c:21:9: note: in expansion of macro ‘__get_user_err’
> 21 | __get_user_err((x), (ptr), __gu_err, TUSER()); \
> | ^~~~~~~~~~~~~~
> t.c:102:16: note: in expansion of macro ‘__get_user’
> 102 | return __get_user(p, ptr);
> | ^~~~~~~~~~
>
> In order for the code you are modifying to be reachable, you need to
> build with CONFIG_CPU_SPECTRE disabled. This is produced by:
>
> int get_ptr(void **ptr)
> {
> void *p;
>
> return __get_user(p, ptr);
> }
Duh, yes. I hate get_user() and I did not notice, because the
allmodconfig build breaks early due to frame size checks, so I was too
lazy to find that config knob and built only a couple of things and an
artificial test case for u64.
But it actually can be solved solvable by switching the casting to:
(x) = *(__force __typeof__(*(ptr)) *) &__gu_val;
Not pretty, but after upping the frame size limit it builds an
allmodconfig kernel.
The proper thing to use the corresponding sized values within the case
$SIZE sections i.e.:
size 1: {
u8 __gu_val;
__get_user_asm_byte(__gu_val, __gu_addr, err, __t);
(x) = *(__force __typeof__(*(ptr)) *) &__gu_val;
break;
}
...
See below.
> Feel free to try to solve this, but I can assure you that you certainly
> are not the first. Several people have already tried.
Obviously not hard enough. :)
Thanks,
tglx
---
Subject: ARM: uaccess: Implement missing __get_user_asm_dword()
From: Thomas Gleixner <tglx@...utronix.de>
Date: Fri, 12 Sep 2025 20:16:11 +0200
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
fixup the size switch case to make get_user(*ptr, **usrptr) work.
Reported-by: kernel test robot <lkp@...el.com>
Signed-off-by: Thomas Gleixner <tglx@...utronix.de>
Cc: Russell King <linux@...linux.org.uk>
Cc: linux-arm-kernel@...ts.infradead.org
Closes: https://lore.kernel.org/oe-kbuild-all/202509120155.pFgwfeUD-lkp@intel.com/
---
V2a: Solve the *ptr issue vs. unsigned long long - Russell
V2: New patch to fix the 0-day fallout
---
arch/arm/include/asm/uaccess.h | 50 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 44 insertions(+), 6 deletions(-)
--- a/arch/arm/include/asm/uaccess.h
+++ b/arch/arm/include/asm/uaccess.h
@@ -286,19 +286,41 @@ extern int __put_user_8(void *, unsigned
#define __get_user_err(x, ptr, err, __t) \
do { \
unsigned long __gu_addr = (unsigned long)(ptr); \
- unsigned long __gu_val; \
unsigned int __ua_flags; \
__chk_user_ptr(ptr); \
might_fault(); \
__ua_flags = uaccess_save_and_enable(); \
switch (sizeof(*(ptr))) { \
- 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; \
- default: (__gu_val) = __get_user_bad(); \
+ case 1: { \
+ u8 __gu_val; \
+ __get_user_asm_byte(__gu_val, __gu_addr, err, __t); \
+ (x) = *(__force __typeof__(*(ptr)) *) &__gu_val; \
+ break; \
+ } \
+ case 2: { \
+ u16 __gu_val; \
+ __get_user_asm_half(__gu_val, __gu_addr, err, __t); \
+ (x) = *(__force __typeof__(*(ptr)) *) &__gu_val; \
+ break; \
+ } \
+ case 4: { \
+ u32 __gu_val; \
+ __get_user_asm_word(__gu_val, __gu_addr, err, __t); \
+ (x) = *(__force __typeof__(*(ptr)) *) &__gu_val; \
+ break; \
+ } \
+ case 8: { \
+ u64 __gu_val; \
+ __get_user_asm_dword(__gu_val, __gu_addr, err, __t); \
+ (x) = *(__force __typeof__(*(ptr)) *) &__gu_val; \
+ break; \
+ } \
+ default: { \
+ unsigned long __gu_val; \
+ (__gu_val) = __get_user_bad(); \
+ } \
} \
uaccess_restore(__ua_flags); \
- (x) = (__typeof__(*(ptr)))__gu_val; \
} while (0)
#endif
@@ -353,6 +375,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); \
Powered by blists - more mailing lists