Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

um: fix _nofault accesses

Nathan reported [1] that when built with clang, the um kernel
crashes pretty much immediately. This turned out to be an issue
with the inline assembly I had added, when clang used %rax/%eax
for both operands. Reorder it so current->thread.segv_continue
is written first, and then the lifetime of _faulted won't have
overlap with the lifetime of segv_continue.

In the email thread Benjamin also pointed out that current->mm
is only NULL for true kernel tasks, but we could do this for a
userspace task, so the current->thread.segv_continue logic must
be lifted out of the mm==NULL check.

Finally, while looking at this, put a barrier() so the NULL
assignment to thread.segv_continue cannot be reorder before
the possibly faulting operation.

Reported-by: Nathan Chancellor <nathan@kernel.org>
Closes: https://lore.kernel.org/r/20250402221254.GA384@ax162 [1]
Fixes: d1d7f01f7cd3 ("um: mark rodata read-only and implement _nofault accesses")
Tested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

+17 -15
+2
arch/um/include/asm/uaccess.h
··· 55 55 goto err_label; \ 56 56 } \ 57 57 *((type *)dst) = get_unaligned((type *)(src)); \ 58 + barrier(); \ 58 59 current->thread.segv_continue = NULL; \ 59 60 } while (0) 60 61 ··· 67 66 if (__faulted) \ 68 67 goto err_label; \ 69 68 put_unaligned(*((type *)src), (type *)(dst)); \ 69 + barrier(); \ 70 70 current->thread.segv_continue = NULL; \ 71 71 } while (0) 72 72
+13 -13
arch/um/kernel/trap.c
··· 225 225 panic("Failed to sync kernel TLBs: %d", err); 226 226 goto out; 227 227 } 228 - else if (current->mm == NULL) { 229 - if (current->pagefault_disabled) { 230 - if (!mc) { 231 - show_regs(container_of(regs, struct pt_regs, regs)); 232 - panic("Segfault with pagefaults disabled but no mcontext"); 233 - } 234 - if (!current->thread.segv_continue) { 235 - show_regs(container_of(regs, struct pt_regs, regs)); 236 - panic("Segfault without recovery target"); 237 - } 238 - mc_set_rip(mc, current->thread.segv_continue); 239 - current->thread.segv_continue = NULL; 240 - goto out; 228 + else if (current->pagefault_disabled) { 229 + if (!mc) { 230 + show_regs(container_of(regs, struct pt_regs, regs)); 231 + panic("Segfault with pagefaults disabled but no mcontext"); 241 232 } 233 + if (!current->thread.segv_continue) { 234 + show_regs(container_of(regs, struct pt_regs, regs)); 235 + panic("Segfault without recovery target"); 236 + } 237 + mc_set_rip(mc, current->thread.segv_continue); 238 + current->thread.segv_continue = NULL; 239 + goto out; 240 + } 241 + else if (current->mm == NULL) { 242 242 show_regs(container_of(regs, struct pt_regs, regs)); 243 243 panic("Segfault with no mm"); 244 244 }
+1 -1
arch/x86/um/shared/sysdep/faultinfo_32.h
··· 31 31 32 32 #define ___backtrack_faulted(_faulted) \ 33 33 asm volatile ( \ 34 - "mov $0, %0\n" \ 35 34 "movl $__get_kernel_nofault_faulted_%=,%1\n" \ 35 + "mov $0, %0\n" \ 36 36 "jmp _end_%=\n" \ 37 37 "__get_kernel_nofault_faulted_%=:\n" \ 38 38 "mov $1, %0;" \
+1 -1
arch/x86/um/shared/sysdep/faultinfo_64.h
··· 31 31 32 32 #define ___backtrack_faulted(_faulted) \ 33 33 asm volatile ( \ 34 - "mov $0, %0\n" \ 35 34 "movq $__get_kernel_nofault_faulted_%=,%1\n" \ 35 + "mov $0, %0\n" \ 36 36 "jmp _end_%=\n" \ 37 37 "__get_kernel_nofault_faulted_%=:\n" \ 38 38 "mov $1, %0;" \