Thread (6 messages) 6 messages, 5 authors, 2026-03-11

Re: [PATCH] powerpc/uaccess: Fix inline assembly for clang build on PPC32

flat view

From: Segher Boessenkool <hidden>
Date: 2026-02-04 00:14:07
Also in: lkml, llvm

Hi!

On Tue, Feb 03, 2026 at 10:19:39PM +0000, David Laight wrote:
On Tue,  3 Feb 2026 08:30:41 +0100
"Christophe Leroy (CS GROUP)" [off-list ref] wrote:
quoted
Test robot reports the following error with clang-16.0.6:

   In file included from kernel/rseq.c:75:
   include/linux/rseq_entry.h:141:3: error: invalid operand for instruction
                   unsafe_get_user(offset, &ucs->post_commit_offset, efault);
                   ^
   include/linux/uaccess.h:608:2: note: expanded from macro 'unsafe_get_user'
           arch_unsafe_get_user(x, ptr, local_label);      \
           ^
   arch/powerpc/include/asm/uaccess.h:518:2: note: expanded from macro 'arch_unsafe_get_user'
           __get_user_size_goto(__gu_val, __gu_addr, sizeof(*(p)), e); \
           ^
   arch/powerpc/include/asm/uaccess.h:284:2: note: expanded from macro '__get_user_size_goto'
           __get_user_size_allowed(x, ptr, size, __gus_retval);    \
           ^
   arch/powerpc/include/asm/uaccess.h:275:10: note: expanded from macro '__get_user_size_allowed'
           case 8: __get_user_asm2(x, (u64 __user *)ptr, retval);  break;  \
                   ^
   arch/powerpc/include/asm/uaccess.h:258:4: note: expanded from macro '__get_user_asm2'
                   "       li %1+1,0\n"                    \
                    ^
   <inline asm>:7:5: note: instantiated into assembly here
           li 31+1,0
              ^
   1 error generated.

On PPC32, for 64 bits vars a pair of registers is used. Usually the
lower register in the pair is the high part and the higher register is
the low part. GCC uses r3/r4 ... r11/r12 ... r14/r15 ... r30/r31

In older kernel code inline assembly was using %1 and %1+1 to represent
64 bits values. However here it looks like clang uses r31 as high part,
allthough r32 doesn't exist hence the error.

Allthoug %1+1 should work, most places now use %L1 instead of %1+1, so
let's do the same here.

With that change, the build doesn't fail anymore and a disassembly shows
clang uses r17/r18 and r31/r14 pair when GCC would have used r16/r17 and
r30/r31:
Isn't it all horribly worse than that?
It only failed because clang picked r31, but if can pick two non-adjacent
registers might it not pick any pair.
In which case there could easily be a 64bit get_user() that reads an incorrect
value and corrupts another register.
Find one and you might have a privilege escalation.
I don't think LLVM is that broken, it only has problems for some edge
cases.  Yes, I might expect too much.  But without proof to the contrary
let's assume things are okay :-)

And, worrying.  But what can we do against it!  Other than never ever
use LLVM for anything serious, of course.


Segher
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help