From 5c8590157b85d4030c40de6c00f9739a676ba8b0 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:13 -1000 Subject: [PATCH 01/48] signal: use scoped user access for old-style sigaction The old sigaction() system call and its compat counterpart do one access_ok() on the user structure and then a series of __get_user() or __put_user() calls on its fields. That was an optimization back when each of those was a single memory access. These days each of them enables and disables user access on its own (stac/clac on x86), so the up-front check saves nothing. Convert them to scoped_user_{read,write}_access() with unsafe_{get,put}_user(), which does the range check once and puts a single stac/clac pair around plain moves. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- kernel/signal.c | 58 +++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/kernel/signal.c b/kernel/signal.c index d31ebcb6ed4d..fb719faf611f 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -4776,12 +4776,12 @@ SYSCALL_DEFINE3(sigaction, int, sig, if (act) { old_sigset_t mask; - if (!access_ok(act, sizeof(*act)) || - __get_user(new_ka.sa.sa_handler, &act->sa_handler) || - __get_user(new_ka.sa.sa_restorer, &act->sa_restorer) || - __get_user(new_ka.sa.sa_flags, &act->sa_flags) || - __get_user(mask, &act->sa_mask)) - return -EFAULT; + scoped_user_read_access(act, Efault) { + unsafe_get_user(new_ka.sa.sa_handler, &act->sa_handler, Efault); + unsafe_get_user(new_ka.sa.sa_restorer, &act->sa_restorer, Efault); + unsafe_get_user(new_ka.sa.sa_flags, &act->sa_flags, Efault); + unsafe_get_user(mask, &act->sa_mask, Efault); + } #ifdef __ARCH_HAS_KA_RESTORER new_ka.ka_restorer = NULL; #endif @@ -4791,15 +4791,18 @@ SYSCALL_DEFINE3(sigaction, int, sig, ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); if (!ret && oact) { - if (!access_ok(oact, sizeof(*oact)) || - __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || - __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer) || - __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || - __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) - return -EFAULT; + scoped_user_write_access(oact, Efault) { + unsafe_put_user(old_ka.sa.sa_handler, &oact->sa_handler, Efault); + unsafe_put_user(old_ka.sa.sa_restorer, &oact->sa_restorer, Efault); + unsafe_put_user(old_ka.sa.sa_flags, &oact->sa_flags, Efault); + unsafe_put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask, Efault); + } } return ret; + +Efault: + return -EFAULT; } #endif #ifdef CONFIG_COMPAT_OLD_SIGACTION @@ -4813,12 +4816,12 @@ COMPAT_SYSCALL_DEFINE3(sigaction, int, sig, compat_uptr_t handler, restorer; if (act) { - if (!access_ok(act, sizeof(*act)) || - __get_user(handler, &act->sa_handler) || - __get_user(restorer, &act->sa_restorer) || - __get_user(new_ka.sa.sa_flags, &act->sa_flags) || - __get_user(mask, &act->sa_mask)) - return -EFAULT; + scoped_user_read_access(act, Efault) { + unsafe_get_user(handler, &act->sa_handler, Efault); + unsafe_get_user(restorer, &act->sa_restorer, Efault); + unsafe_get_user(new_ka.sa.sa_flags, &act->sa_flags, Efault); + unsafe_get_user(mask, &act->sa_mask, Efault); + } #ifdef __ARCH_HAS_KA_RESTORER new_ka.ka_restorer = NULL; @@ -4831,16 +4834,19 @@ COMPAT_SYSCALL_DEFINE3(sigaction, int, sig, ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); if (!ret && oact) { - if (!access_ok(oact, sizeof(*oact)) || - __put_user(ptr_to_compat(old_ka.sa.sa_handler), - &oact->sa_handler) || - __put_user(ptr_to_compat(old_ka.sa.sa_restorer), - &oact->sa_restorer) || - __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || - __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) - return -EFAULT; + scoped_user_write_access(oact, Efault) { + unsafe_put_user(ptr_to_compat(old_ka.sa.sa_handler), + &oact->sa_handler, Efault); + unsafe_put_user(ptr_to_compat(old_ka.sa.sa_restorer), + &oact->sa_restorer, Efault); + unsafe_put_user(old_ka.sa.sa_flags, &oact->sa_flags, Efault); + unsafe_put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask, Efault); + } } return ret; + +Efault: + return -EFAULT; } #endif From b040da2a0046ae64eeeed256b4465f231d5ee6de Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:16 -1000 Subject: [PATCH 02/48] signal: use scoped user access in __save_altstack() __save_altstack() and __compat_save_altstack() write three fields with __put_user(), relying on the caller having done access_ok() on the whole signal frame. Each of those writes enables and disables user access on its own, so that check saves nothing. x86 and powerpc already avoid these functions by using unsafe_save_altstack() inside their own user access regions. Use the same helpers here, inside a scoped_user_write_access(), so that every other architecture gets a single range check and a single user access region too. None of those callers is itself inside a user access region, so nothing nests. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- kernel/signal.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/kernel/signal.c b/kernel/signal.c index fb719faf611f..fa652f7af148 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -4539,11 +4539,12 @@ int restore_altstack(const stack_t __user *uss) int __save_altstack(stack_t __user *uss, unsigned long sp) { - struct task_struct *t = current; - int err = __put_user((void __user *)t->sas_ss_sp, &uss->ss_sp) | - __put_user(t->sas_ss_flags, &uss->ss_flags) | - __put_user(t->sas_ss_size, &uss->ss_size); - return err; + scoped_user_write_access(uss, Efault) + unsafe_save_altstack(uss, sp, Efault); + return 0; + +Efault: + return -EFAULT; } #ifdef CONFIG_COMPAT @@ -4592,13 +4593,12 @@ int compat_restore_altstack(const compat_stack_t __user *uss) int __compat_save_altstack(compat_stack_t __user *uss, unsigned long sp) { - int err; - struct task_struct *t = current; - err = __put_user(ptr_to_compat((void __user *)t->sas_ss_sp), - &uss->ss_sp) | - __put_user(t->sas_ss_flags, &uss->ss_flags) | - __put_user(t->sas_ss_size, &uss->ss_size); - return err; + scoped_user_write_access(uss, Efault) + unsafe_compat_save_altstack(uss, sp, Efault); + return 0; + +Efault: + return -EFAULT; } #endif From ae78eff333746326e630e505d7fac0d237e644ef Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:18 -1000 Subject: [PATCH 03/48] compat: use scoped user access in get_compat_sigevent() get_compat_sigevent() does one access_ok() on the user structure and then reads four fields with __get_user(). Each of those reads enables and disables user access on its own, so the single range check no longer buys anything. Read the fields inside a scoped_user_read_access() instead. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- kernel/compat.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/kernel/compat.c b/kernel/compat.c index fb50f29d9b36..c754793c7d65 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -180,14 +180,18 @@ int get_compat_sigevent(struct sigevent *event, const struct compat_sigevent __user *u_event) { memset(event, 0, sizeof(*event)); - return (!access_ok(u_event, sizeof(*u_event)) || - __get_user(event->sigev_value.sival_int, - &u_event->sigev_value.sival_int) || - __get_user(event->sigev_signo, &u_event->sigev_signo) || - __get_user(event->sigev_notify, &u_event->sigev_notify) || - __get_user(event->sigev_notify_thread_id, - &u_event->sigev_notify_thread_id)) - ? -EFAULT : 0; + scoped_user_read_access(u_event, Efault) { + unsafe_get_user(event->sigev_value.sival_int, + &u_event->sigev_value.sival_int, Efault); + unsafe_get_user(event->sigev_signo, &u_event->sigev_signo, Efault); + unsafe_get_user(event->sigev_notify, &u_event->sigev_notify, Efault); + unsafe_get_user(event->sigev_notify_thread_id, + &u_event->sigev_notify_thread_id, Efault); + } + return 0; + +Efault: + return -EFAULT; } long compat_get_bitmap(unsigned long *mask, const compat_ulong_t __user *umask, From edc483b611264ec6fd3b522a477d5fb1250299da Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:20 -1000 Subject: [PATCH 04/48] ptrace: use plain get_user()/put_user() for the regset iovec PTRACE_GETREGSET and PTRACE_SETREGSET do an access_ok() on the user iovec, read its two fields with __get_user(), and once the regset has been transferred write the length back with __put_user(). Each of those accesses enables and disables user access on its own, so the up-front check saves nothing. On x86, __get_user() is even more expensive than get_user(): it does an lfence, where get_user() checks the address with a couple of plain instructions. Just use get_user() and put_user(), and drop the access_ok(). Both fields are read before anything else is done, so a bad pointer still fails with -EFAULT up front. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- kernel/ptrace.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/kernel/ptrace.c b/kernel/ptrace.c index d041645d9d17..b632f9f7979c 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -1359,16 +1359,13 @@ int ptrace_request(struct task_struct *child, long request, struct iovec kiov; struct iovec __user *uiov = datavp; - if (!access_ok(uiov, sizeof(*uiov))) - return -EFAULT; - - if (__get_user(kiov.iov_base, &uiov->iov_base) || - __get_user(kiov.iov_len, &uiov->iov_len)) + if (get_user(kiov.iov_base, &uiov->iov_base) || + get_user(kiov.iov_len, &uiov->iov_len)) return -EFAULT; ret = ptrace_regset(child, request, addr, &kiov); if (!ret) - ret = __put_user(kiov.iov_len, &uiov->iov_len); + ret = put_user(kiov.iov_len, &uiov->iov_len); break; } @@ -1525,11 +1522,8 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request, compat_uptr_t ptr; compat_size_t len; - if (!access_ok(uiov, sizeof(*uiov))) - return -EFAULT; - - if (__get_user(ptr, &uiov->iov_base) || - __get_user(len, &uiov->iov_len)) + if (get_user(ptr, &uiov->iov_base) || + get_user(len, &uiov->iov_len)) return -EFAULT; kiov.iov_base = compat_ptr(ptr); @@ -1537,7 +1531,7 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request, ret = ptrace_regset(child, request, addr, &kiov); if (!ret) - ret = __put_user(kiov.iov_len, &uiov->iov_len); + ret = put_user(kiov.iov_len, &uiov->iov_len); break; } #endif From 678a0299d81c2ce8ef12f55ef7a722026b38a6e0 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:24 -1000 Subject: [PATCH 05/48] uprobes: Use get_user() in is_trap_at_addr() is_trap_at_addr() reads the instruction at a user address with __get_user(), inside pagefault_disable() since the caller holds mmap_lock. There is no access_ok() in front of it, and nothing here needs the unchecked version: with page faults disabled, get_user()'s might_fault() check returns early, so that is no reason to avoid it either. Use get_user(), which also checks that the address is a user address, and on x86 avoids the lfence that __get_user() does. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- kernel/events/uprobes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 7709ea882477..3192806eebfe 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -2404,7 +2404,7 @@ static int is_trap_at_addr(struct mm_struct *mm, unsigned long vaddr) return -EINVAL; pagefault_disable(); - result = __get_user(opcode, (uprobe_opcode_t __user *)vaddr); + result = get_user(opcode, (uprobe_opcode_t __user *)vaddr); pagefault_enable(); if (likely(result == 0)) From 9ef61ff7ca4cad2194eb3fd3de99a200f4038833 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:28 -1000 Subject: [PATCH 06/48] syscall_user_dispatch: Use get_user() for the selector syscall_user_dispatch() reads the selector byte with __get_user(), relying on an access_ok() done once at prctl time when the selector address was set. So the pointer being dereferenced was validated in a different system call, and the separation saves nothing anyway: __get_user() still enables and disables user access, and on x86 it does an lfence that get_user() avoids. Use get_user(), and drop the comment explaining why the unchecked access was safe. The selector may be a tagged address. The prctl-time check is done on untagged_addr(selector), but it is the tagged pointer that gets stored, and __get_user() never checked it. get_user() does, and on x86 with LAM a tagged pointer fails its range check. So untag it at the point of use as well. The prctl-time check stays, so a bad address is still reported when it is set. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- kernel/entry/syscall_user_dispatch.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/kernel/entry/syscall_user_dispatch.c b/kernel/entry/syscall_user_dispatch.c index 59c861866941..7d1dd7f1bf38 100644 --- a/kernel/entry/syscall_user_dispatch.c +++ b/kernel/entry/syscall_user_dispatch.c @@ -45,11 +45,7 @@ bool syscall_user_dispatch(struct pt_regs *regs) return false; if (likely(sd->selector)) { - /* - * access_ok() is performed once, at prctl time, when - * the selector is loaded by userspace. - */ - if (unlikely(__get_user(state, sd->selector))) { + if (unlikely(get_user(state, untagged_addr(sd->selector)))) { force_exit_sig(SIGSEGV); return true; } From 94905fcbd9e225dd931aa543071d2fa55704206a Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:30 -1000 Subject: [PATCH 07/48] bpf: Use get_user() for kprobe_multi symbols and uprobe_multi arrays copy_user_syms() and bpf_uprobe_multi_link_attach() check whole user arrays with access_ok() up front, then read them one element at a time with __get_user(), with other work in between: allocations and strncpy_from_user() in copy_user_syms(), validation in the uprobe_multi loop. Each of those accesses enables and disables user access on its own, so the up-front check saves nothing. On x86, __get_user() is even more expensive than get_user(): it does an lfence, where get_user() checks the address with a couple of plain instructions. Use get_user(). The access_ok() checks stay, because they also make a bad array fail with -EFAULT before anything else is looked up or allocated. Update the comment that said they were there for __get_user(). This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- kernel/trace/bpf_trace.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 29260951aa87..88c3b5b513ab 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -2388,7 +2388,7 @@ static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32 goto error; for (p = buf, i = 0; i < cnt; i++) { - if (__get_user(usymbol, usyms + i)) { + if (get_user(usymbol, usyms + i)) { err = -EFAULT; goto error; } @@ -3295,8 +3295,8 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr /* * All uoffsets/uref_ctr_offsets/ucookies arrays have the same value - * size, we need to check their address range is safe for __get_user - * calls. + * size. Check their address ranges up front, so that a bad pointer + * fails before anything is looked up or allocated. */ size = sizeof(*uoffsets) * cnt; if (!access_ok(uoffsets, size) || !access_ok(uref_ctr_offsets, size) || @@ -3331,7 +3331,7 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr goto error_free; for (i = 0; i < cnt; i++) { - if (__get_user(uprobes[i].offset, uoffsets + i)) { + if (get_user(uprobes[i].offset, uoffsets + i)) { err = -EFAULT; goto error_free; } @@ -3339,11 +3339,11 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr err = -EINVAL; goto error_free; } - if (uref_ctr_offsets && __get_user(uprobes[i].ref_ctr_offset, uref_ctr_offsets + i)) { + if (uref_ctr_offsets && get_user(uprobes[i].ref_ctr_offset, uref_ctr_offsets + i)) { err = -EFAULT; goto error_free; } - if (ucookies && __get_user(uprobes[i].cookie, ucookies + i)) { + if (ucookies && get_user(uprobes[i].cookie, ucookies + i)) { err = -EFAULT; goto error_free; } From 4e60e85129ae142720ae1fd12c535317a64906b2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:33 -1000 Subject: [PATCH 08/48] net: use put_user() for msghdr results written back to user space ____sys_recvmsg() writes msg_flags and msg_controllen back with __put_user(), and the compat sendmmsg()/recvmmsg() loops write msg_len the same way. None of these has an access_ok() of its own: they rely on the earlier copy_from_user() of the message header having checked the range. For the compat msg_len that is not even true, since msg_len lives after the compat_msghdr that was copied. It is only safe because compat pointers are 32-bit. The native mmsg paths already use put_user(). Use put_user() everywhere. On x86 that adds a couple of instructions to check the address, on a path that has to enable and disable user access for each write anyway. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- net/socket.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/net/socket.c b/net/socket.c index c05d86e63abf..fe09cca00805 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2850,7 +2850,7 @@ int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, &msg_sys, flags, &used_address, MSG_EOR); if (err < 0) break; - err = __put_user(err, &compat_entry->msg_len); + err = put_user(err, &compat_entry->msg_len); ++compat_entry; } else { err = ___sys_sendmsg(sock, @@ -2943,16 +2943,16 @@ static int ____sys_recvmsg(struct socket *sock, struct msghdr *msg_sys, if (err < 0) goto out; } - err = __put_user((msg_sys->msg_flags & ~MSG_CMSG_COMPAT), - COMPAT_FLAGS(msg)); + err = put_user((msg_sys->msg_flags & ~MSG_CMSG_COMPAT), + COMPAT_FLAGS(msg)); if (err) goto out; if (MSG_CMSG_COMPAT & flags) - err = __put_user((unsigned long)msg_sys->msg_control - cmsg_ptr, - &msg_compat->msg_controllen); + err = put_user((unsigned long)msg_sys->msg_control - cmsg_ptr, + &msg_compat->msg_controllen); else - err = __put_user((unsigned long)msg_sys->msg_control - cmsg_ptr, - &msg->msg_controllen); + err = put_user((unsigned long)msg_sys->msg_control - cmsg_ptr, + &msg->msg_controllen); if (err) goto out; err = len; @@ -3064,7 +3064,7 @@ static int do_recvmmsg(int fd, struct mmsghdr __user *mmsg, datagrams); if (err < 0) break; - err = __put_user(err, &compat_entry->msg_len); + err = put_user(err, &compat_entry->msg_len); ++compat_entry; } else { err = ___sys_recvmsg(sock, From 2806617c9c02fe16b7b3a93a98e7907d53c1cf31 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:37 -1000 Subject: [PATCH 09/48] x86/entry: Use get_user() for the fast 32-bit syscall EBP fetch __do_fast_syscall_32() fetches the user's EBP from the stack slot the vDSO stashed it in. On 64-bit kernels it used __get_user(), as a micro-optimization: the address is explicitly 32 bits, so it cannot be out of range. That optimization has turned into a pessimization. On x86-64, get_user() checks the address with a compare and a conditional move, while __get_user() does an lfence instead. Use get_user() on both 32-bit and 64-bit, which also removes the IS_ENABLED(CONFIG_X86_64) special case. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/x86/entry/syscall_32.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/arch/x86/entry/syscall_32.c b/arch/x86/entry/syscall_32.c index 91123a90710c..a59509bb460a 100644 --- a/arch/x86/entry/syscall_32.c +++ b/arch/x86/entry/syscall_32.c @@ -265,17 +265,8 @@ static noinstr bool __do_fast_syscall_32(struct pt_regs *regs) instrumentation_begin(); local_irq_enable(); /* Fetch EBP from where the vDSO stashed it. */ - if (IS_ENABLED(CONFIG_X86_64)) { - /* - * Micro-optimization: the pointer we're following is - * explicitly 32 bits, so it can't be out of range. - */ - res = __get_user(*(u32 *)®s->bp, - (u32 __user __force *)(unsigned long)(u32)regs->sp); - } else { - res = get_user(*(u32 *)®s->bp, + res = get_user(*(u32 *)®s->bp, (u32 __user __force *)(unsigned long)(u32)regs->sp); - } if (res) { /* User code screwed up. */ From 9da9f342a897a2d43ad947d4073b1912800ab436 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:39 -1000 Subject: [PATCH 10/48] x86/signal: Use scoped user access in the sigreturn system calls All four sigreturn variants (32-bit sigreturn and rt_sigreturn, native 64-bit and x32 rt_sigreturn) do an access_ok() on the whole signal frame only to read the signal mask with __get_user(). The 64-bit and x32 variants also read uc_flags that way. Everything else in the frame is read by helpers that do their own checking. Read those fields inside a scoped_user_read_access() instead, which does the range check once and puts a single stac/clac pair around plain loads. The scoped accessor shadows 'frame' with a checked copy only inside its block, so the badframe path still reports the original pointer. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/x86/kernel/signal_32.c | 15 ++++++--------- arch/x86/kernel/signal_64.c | 20 ++++++++------------ 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/arch/x86/kernel/signal_32.c b/arch/x86/kernel/signal_32.c index 8e87ab586437..0e97ef61fe30 100644 --- a/arch/x86/kernel/signal_32.c +++ b/arch/x86/kernel/signal_32.c @@ -154,11 +154,10 @@ SYSCALL32_DEFINE0(sigreturn) prevent_single_step_upon_eretu(regs); - if (!access_ok(frame, sizeof(*frame))) - goto badframe; - if (__get_user(set.sig[0], &frame->sc.oldmask) - || __get_user(((__u32 *)&set)[1], &frame->extramask[0])) - goto badframe; + scoped_user_read_access(frame, badframe) { + unsafe_get_user(set.sig[0], &frame->sc.oldmask, badframe); + unsafe_get_user(((__u32 *)&set)[1], &frame->extramask[0], badframe); + } set_current_blocked(&set); @@ -181,10 +180,8 @@ SYSCALL32_DEFINE0(rt_sigreturn) frame = (struct rt_sigframe_ia32 __user *)(regs->sp - 4); - if (!access_ok(frame, sizeof(*frame))) - goto badframe; - if (__get_user(*(__u64 *)&set, (__u64 __user *)&frame->uc.uc_sigmask)) - goto badframe; + scoped_user_read_access(frame, badframe) + unsafe_get_user(*(__u64 *)&set, (__u64 __user *)&frame->uc.uc_sigmask, badframe); set_current_blocked(&set); diff --git a/arch/x86/kernel/signal_64.c b/arch/x86/kernel/signal_64.c index d483b585c6c6..62cc62d8e092 100644 --- a/arch/x86/kernel/signal_64.c +++ b/arch/x86/kernel/signal_64.c @@ -253,12 +253,10 @@ SYSCALL_DEFINE0(rt_sigreturn) prevent_single_step_upon_eretu(regs); frame = (struct rt_sigframe __user *)(regs->sp - sizeof(long)); - if (!access_ok(frame, sizeof(*frame))) - goto badframe; - if (__get_user(*(__u64 *)&set, (__u64 __user *)&frame->uc.uc_sigmask)) - goto badframe; - if (__get_user(uc_flags, &frame->uc.uc_flags)) - goto badframe; + scoped_user_read_access(frame, badframe) { + unsafe_get_user(*(__u64 *)&set, (__u64 __user *)&frame->uc.uc_sigmask, badframe); + unsafe_get_user(uc_flags, &frame->uc.uc_flags, badframe); + } set_current_blocked(&set); @@ -372,12 +370,10 @@ COMPAT_SYSCALL_DEFINE0(x32_rt_sigreturn) frame = (struct rt_sigframe_x32 __user *)(regs->sp - 8); - if (!access_ok(frame, sizeof(*frame))) - goto badframe; - if (__get_user(set.sig[0], (__u64 __user *)&frame->uc.uc_sigmask)) - goto badframe; - if (__get_user(uc_flags, &frame->uc.uc_flags)) - goto badframe; + scoped_user_read_access(frame, badframe) { + unsafe_get_user(set.sig[0], (__u64 __user *)&frame->uc.uc_sigmask, badframe); + unsafe_get_user(uc_flags, &frame->uc.uc_flags, badframe); + } set_current_blocked(&set); From 7941b97fdd890fb027a1b6f589e9d86c7ca8a33f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:42 -1000 Subject: [PATCH 11/48] x86/stacktrace: Use scoped user access in copy_stack_frame() copy_stack_frame() reads the two words of a user stack frame with __get_user(), after an __access_ok() on the frame and inside pagefault_disable(), since the user stack walk can run in atomic context. Do the two reads in a scoped_user_read_access() instead. It nests inside the pagefault_disable() section, does the range check itself, and needs only a single stac/clac pair for both loads. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/x86/kernel/stacktrace.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/arch/x86/kernel/stacktrace.c b/arch/x86/kernel/stacktrace.c index ee117fcf46ed..9412ac4ddf46 100644 --- a/arch/x86/kernel/stacktrace.c +++ b/arch/x86/kernel/stacktrace.c @@ -88,16 +88,15 @@ static int copy_stack_frame(const struct stack_frame_user __user *fp, struct stack_frame_user *frame) { - int ret; + int ret = 0; - if (!__access_ok(fp, sizeof(*frame))) - return 0; - - ret = 1; pagefault_disable(); - if (__get_user(frame->next_fp, &fp->next_fp) || - __get_user(frame->ret_addr, &fp->ret_addr)) - ret = 0; + scoped_user_read_access(fp, out) { + unsafe_get_user(frame->next_fp, &fp->next_fp, out); + unsafe_get_user(frame->ret_addr, &fp->ret_addr, out); + } + ret = 1; +out: pagefault_enable(); return ret; From 0f697d307cab76512f504b3aa0aa5df0205fd50b Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:44 -1000 Subject: [PATCH 12/48] perf/x86: Use scoped user access for the user callchain frames The user callchain walkers read each two-word stack frame with __get_user(), after checking the frame with valid_user_frame(), which is just __access_ok(). They run with page faults disabled, since they can be called from NMI context. Read each frame inside a scoped_user_read_access() instead, which does the range check itself and needs only a single stac/clac pair for both loads. That leaves valid_user_frame() unused, so remove it. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/x86/events/core.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c index 8b3ea0adb965..ded51c72e544 100644 --- a/arch/x86/events/core.c +++ b/arch/x86/events/core.c @@ -2902,12 +2902,6 @@ perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, struct pt_regs *re } } -static inline int -valid_user_frame(const void __user *fp, unsigned long size) -{ - return __access_ok(fp, size); -} - static unsigned long get_segment_base(unsigned int segment) { struct desc_struct *desc; @@ -2971,17 +2965,15 @@ perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry_ctx *ent perf_callchain_store(entry, ret_addr); while (entry->nr < entry->max_stack) { - if (!valid_user_frame(fp, sizeof(frame))) - break; - - if (__get_user(frame.next_frame, &fp->next_frame)) - break; - if (__get_user(frame.return_address, &fp->return_address)) - break; + scoped_user_read_access(fp, out) { + unsafe_get_user(frame.next_frame, &fp->next_frame, out); + unsafe_get_user(frame.return_address, &fp->return_address, out); + } perf_callchain_store(entry, cs_base + frame.return_address); fp = compat_ptr(ss_base + frame.next_frame); } +out: pagefault_enable(); return 1; } @@ -3036,17 +3028,15 @@ perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs perf_callchain_store(entry, ret_addr); while (entry->nr < entry->max_stack) { - if (!valid_user_frame(fp, sizeof(frame))) - break; - - if (__get_user(frame.next_frame, &fp->next_frame)) - break; - if (__get_user(frame.return_address, &fp->return_address)) - break; + scoped_user_read_access(fp, out) { + unsafe_get_user(frame.next_frame, &fp->next_frame, out); + unsafe_get_user(frame.return_address, &fp->return_address, out); + } perf_callchain_store(entry, frame.return_address); fp = (void __user *)frame.next_frame; } +out: pagefault_enable(); } From 05c6ad3f3bced959e10ed28ec6e1088d1a5bd524 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:47 -1000 Subject: [PATCH 13/48] x86/ptrace: Use get_user() in the general register set writers genregs_set() and genregs32_set() read the user buffer one word at a time with __get_user(), calling putreg() for each. The access_ok() that makes this safe is in the generic copy_regset_from_user(), on the other side of an indirect call through the regset. Each of those accesses enables and disables user access on its own, so the up-front check saves nothing. On x86, __get_user() is even more expensive than get_user(): it does an lfence, where get_user() checks the address with a couple of plain instructions. Use get_user(), so each read checks its own address. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/x86/kernel/ptrace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 3dcadc13f09a..953d577ebadd 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -457,7 +457,7 @@ static int genregs_set(struct task_struct *target, const unsigned long __user *u = ubuf; while (count >= sizeof(*u) && !ret) { unsigned long word; - ret = __get_user(word, u++); + ret = get_user(word, u++); if (ret) break; ret = putreg(target, pos, word); @@ -1050,7 +1050,7 @@ static int genregs32_set(struct task_struct *target, const compat_ulong_t __user *u = ubuf; while (count >= sizeof(*u) && !ret) { compat_ulong_t word; - ret = __get_user(word, u++); + ret = get_user(word, u++); if (ret) break; ret = putreg32(target, pos, word); From a7de2d2d783b4e3d29e89ddea52798d3172e4f07 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:49 -1000 Subject: [PATCH 14/48] x86/fpu: Use get_user()/put_user() for the signal frame fields The FPU signal frame code reads and writes a handful of individual fields (the xstate magic words, the legacy status word, the xfeatures header and PKRU) with __get_user() and __put_user(). These rely on an access_ok() of the whole frame done earlier by the caller. Each of those accesses enables and disables user access on its own, so the up-front check saves nothing. On x86, __get_user() is even more expensive than get_user(): it does an lfence, where get_user() checks the address with a couple of plain instructions. Use get_user() and put_user(). The accesses that run with page faults disabled stay that way; get_user() and put_user() are fine there. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/x86/kernel/fpu/signal.c | 12 ++++++------ arch/x86/kernel/fpu/xstate.h | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c index 33e1284bf3e4..0a82a39ef9ae 100644 --- a/arch/x86/kernel/fpu/signal.c +++ b/arch/x86/kernel/fpu/signal.c @@ -48,7 +48,7 @@ static inline bool check_xstate_in_sigframe(struct fxregs_state __user *fxbuf, * fpstate layout with out copying the extended state information * in the memory layout. */ - if (__get_user(magic2, (__u32 __user *)(fpstate + fx_sw->xstate_size))) + if (get_user(magic2, (__u32 __user *)(fpstate + fx_sw->xstate_size))) return false; if (likely(magic2 == FP_XSTATE_MAGIC2)) @@ -81,14 +81,14 @@ static inline bool save_fsave_header(struct task_struct *tsk, void __user *buf) convert_from_fxsr(&env, tsk); if (__copy_to_user(buf, &env, sizeof(env)) || - __put_user(xsave->i387.swd, &fp->status) || - __put_user(X86_FXSR_MAGIC, &fp->magic)) + put_user(xsave->i387.swd, &fp->status) || + put_user(X86_FXSR_MAGIC, &fp->magic)) return false; } else { struct fregs_state __user *fp = buf; u32 swd; - if (__get_user(swd, &fp->swd) || __put_user(swd, &fp->status)) + if (get_user(swd, &fp->swd) || put_user(swd, &fp->status)) return false; } @@ -128,8 +128,8 @@ static inline bool save_xstate_epilog(void __user *buf, int ia32_frame, if (!use_xsave()) return !err; - err |= __put_user(FP_XSTATE_MAGIC2, - (__u32 __user *)(buf + fpstate->user_size)); + err |= put_user(FP_XSTATE_MAGIC2, + (__u32 __user *)(buf + fpstate->user_size)); /* * For legacy compatible, we always set FP/SSE bits in the bit diff --git a/arch/x86/kernel/fpu/xstate.h b/arch/x86/kernel/fpu/xstate.h index 38a2862f09d3..1ea762e93086 100644 --- a/arch/x86/kernel/fpu/xstate.h +++ b/arch/x86/kernel/fpu/xstate.h @@ -76,9 +76,9 @@ static inline int set_xfeature_in_sigframe(struct xregs_state __user *xbuf, u64 int err; /* Read the xfeatures value already saved in the user buffer */ - err = __get_user(xfeatures, &xbuf->header.xfeatures); + err = get_user(xfeatures, &xbuf->header.xfeatures); xfeatures |= mask; - err |= __put_user(xfeatures, &xbuf->header.xfeatures); + err |= put_user(xfeatures, &xbuf->header.xfeatures); return err; } @@ -99,7 +99,7 @@ static inline int update_pkru_in_sigframe(struct xregs_state __user *buf, u32 pk return err; /* Update PKRU value in the userspace xsave buffer. */ - return __put_user(pkru, (unsigned int __user *)get_xsave_addr_user(buf, XFEATURE_PKRU)); + return put_user(pkru, (unsigned int __user *)get_xsave_addr_user(buf, XFEATURE_PKRU)); } /* XSAVE/XRSTOR wrapper functions */ From 41c83aee073fdf45536fa27cafcda499ad64a478 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:52 -1000 Subject: [PATCH 15/48] x86/xen: Fix a stale __get_user() reference in a comment The comment in set_aliased_prot() says the target address is faulted in with __get_user(), but the code has used copy_from_kernel_nofault() for a long time. Make the comment say so. Signed-off-by: Linus Torvalds --- arch/x86/xen/enlighten_pv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/xen/enlighten_pv.c b/arch/x86/xen/enlighten_pv.c index 2c64b388f616..c5117a7730d6 100644 --- a/arch/x86/xen/enlighten_pv.c +++ b/arch/x86/xen/enlighten_pv.c @@ -475,9 +475,9 @@ static void set_aliased_prot(void *v, pgprot_t prot) * have the same nominal mm, but if we're a kernel thread, lazy * mm dropping could change our pgd. * - * Out of an abundance of caution, this uses __get_user() to fault - * in the target address just in case there's some obscure case - * in which the target address isn't readable. + * Out of an abundance of caution, this uses copy_from_kernel_nofault() + * to fault in the target address just in case there's some obscure + * case in which the target address isn't readable. */ preempt_disable(); From 8d1cec6c767053c9bcb8821aedb4c4bd47f54f7f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:55 -1000 Subject: [PATCH 16/48] drivers/char/mem: use get_user()/put_user() for /dev/port byte copies read_port() and write_port() check the whole user buffer with access_ok() and then move it one byte at a time with __put_user() and __get_user(), doing an inb() or outb() for each. Each of those accesses enables and disables user access on its own, so the up-front check saves nothing. Use put_user() and get_user(). The access_ok() stays, because it also makes a partly invalid buffer fail up front, rather than after some ports have already been read or written. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- drivers/char/mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 5b93c92c2cf1..c4c78182b56e 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -372,7 +372,7 @@ static ssize_t read_port(struct file *file, char __user *buf, if (!access_ok(buf, count)) return -EFAULT; while (count-- > 0 && i < 65536) { - if (__put_user(inb(i), tmp) < 0) + if (put_user(inb(i), tmp) < 0) return -EFAULT; i++; tmp++; @@ -392,7 +392,7 @@ static ssize_t write_port(struct file *file, const char __user *buf, while (count-- > 0 && i < 65536) { char c; - if (__get_user(c, tmp)) { + if (get_user(c, tmp)) { if (tmp > buf) break; return -EFAULT; From 198db29840c4cbc617da105bfea72368e0baa8ea Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:58 -1000 Subject: [PATCH 17/48] PCI/proc: Use get_user()/put_user() for config space reads and writes proc_bus_pci_read() and proc_bus_pci_write() check the user buffer with access_ok() and then copy config space one byte, word or dword at a time with __put_user() and __get_user(). Each of those accesses enables and disables user access on its own, so the up-front check saves nothing. Use put_user() and get_user(). This deliberately changes nothing else: the return values were ignored before and still are, and the access_ok() stays, since it is the only error these functions report for a bad buffer. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- drivers/pci/proc.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index f5fd860a0540..3d7a9ed5fc22 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -67,7 +67,7 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf, if ((pos & 1) && cnt) { unsigned char val; pci_user_read_config_byte(dev, pos, &val); - __put_user(val, buf); + put_user(val, buf); buf++; pos++; cnt--; @@ -76,7 +76,7 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf, if ((pos & 3) && cnt > 2) { unsigned short val; pci_user_read_config_word(dev, pos, &val); - __put_user(cpu_to_le16(val), (__le16 __user *) buf); + put_user(cpu_to_le16(val), (__le16 __user *) buf); buf += 2; pos += 2; cnt -= 2; @@ -85,7 +85,7 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf, while (cnt >= 4) { unsigned int val; pci_user_read_config_dword(dev, pos, &val); - __put_user(cpu_to_le32(val), (__le32 __user *) buf); + put_user(cpu_to_le32(val), (__le32 __user *) buf); buf += 4; pos += 4; cnt -= 4; @@ -95,7 +95,7 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf, if (cnt >= 2) { unsigned short val; pci_user_read_config_word(dev, pos, &val); - __put_user(cpu_to_le16(val), (__le16 __user *) buf); + put_user(cpu_to_le16(val), (__le16 __user *) buf); buf += 2; pos += 2; cnt -= 2; @@ -104,7 +104,7 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf, if (cnt) { unsigned char val; pci_user_read_config_byte(dev, pos, &val); - __put_user(val, buf); + put_user(val, buf); pos++; } @@ -151,7 +151,7 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf, if ((pos & 1) && cnt) { unsigned char val; - __get_user(val, buf); + get_user(val, buf); pci_user_write_config_byte(dev, pos, val); buf++; pos++; @@ -160,7 +160,7 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf, if ((pos & 3) && cnt > 2) { __le16 val; - __get_user(val, (__le16 __user *) buf); + get_user(val, (__le16 __user *) buf); pci_user_write_config_word(dev, pos, le16_to_cpu(val)); buf += 2; pos += 2; @@ -169,7 +169,7 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf, while (cnt >= 4) { __le32 val; - __get_user(val, (__le32 __user *) buf); + get_user(val, (__le32 __user *) buf); pci_user_write_config_dword(dev, pos, le32_to_cpu(val)); buf += 4; pos += 4; @@ -178,7 +178,7 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf, if (cnt >= 2) { __le16 val; - __get_user(val, (__le16 __user *) buf); + get_user(val, (__le16 __user *) buf); pci_user_write_config_word(dev, pos, le16_to_cpu(val)); buf += 2; pos += 2; @@ -187,7 +187,7 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf, if (cnt) { unsigned char val; - __get_user(val, buf); + get_user(val, buf); pci_user_write_config_byte(dev, pos, val); pos++; } From d1280054f8374cedc1d3b2fac44eb060f81cba50 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:24:01 -1000 Subject: [PATCH 18/48] PNP: isapnp: use put_user() in the proc read loop isapnp_proc_bus_read() checks the user buffer with access_ok() and then copies the configuration registers one byte at a time with __put_user(), reading each register in between. Each of those accesses enables and disables user access on its own, so the up-front check saves nothing. Use put_user(). The ignored return value and the access_ok() stay as they were. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- drivers/pnp/isapnp/proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pnp/isapnp/proc.c b/drivers/pnp/isapnp/proc.c index 55ae72a2818b..c1f6e6bd0eb2 100644 --- a/drivers/pnp/isapnp/proc.c +++ b/drivers/pnp/isapnp/proc.c @@ -41,7 +41,7 @@ static ssize_t isapnp_proc_bus_read(struct file *file, char __user * buf, for (; pos < 256 && cnt > 0; pos++, buf++, cnt--) { unsigned char val; val = isapnp_read_byte(pos); - __put_user(val, buf); + put_user(val, buf); } isapnp_cfg_end(); From e0d5fa9ae40bedfce4a7b7b2f6ec22096650ffdf Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:24:03 -1000 Subject: [PATCH 19/48] ubi: use put_user() when filling the erase counter array ubi_get_ec_info() checks the user's erase_counters array with access_ok() and then fills it one entry at a time with __put_user(), taking the wear-leveling lock for each entry in between. Each of those accesses enables and disables user access on its own, so the up-front check saves nothing. Use put_user(). The access_ok() stays, so a bad array still fails before anything is written. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- drivers/mtd/ubi/cdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 54acd8394d67..8dd005ef376b 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -861,7 +861,7 @@ static int ubi_get_ec_info(struct ubi_device *ubi, struct ubi_ecinfo_req __user int ec; if (ubi_io_is_bad(ubi, peb)) { - if (__put_user(UBI_UNKNOWN, ureq->erase_counters+read_cnt)) + if (put_user(UBI_UNKNOWN, ureq->erase_counters+read_cnt)) return -EFAULT; continue; @@ -877,7 +877,7 @@ static int ubi_get_ec_info(struct ubi_device *ubi, struct ubi_ecinfo_req __user spin_unlock(&ubi->wl_lock); - if (__put_user(ec, ureq->erase_counters+read_cnt)) + if (put_user(ec, ureq->erase_counters+read_cnt)) return -EFAULT; } From 05fb07c40e0013ba5ae373c6ea1bcc622eaea7f0 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:24:05 -1000 Subject: [PATCH 20/48] vdpa_sim: disable page faults around vringh_need_notify_iotlb() When vdpa_sim runs with use_va (the default), its rings live in user memory, and vringh reads the ring's flags and event index with __get_user() through the user address. The net and block simulators call vringh_need_notify_iotlb() with bottom halves disabled, so those reads happen in atomic context. That only works today because __get_user() has no might_fault() check: a fault is not handled in atomic context and simply comes back as -EFAULT. Make that explicit with pagefault_disable() around the call, so vringh can use the normal user accessors without complaint. Only the notification check needs this: the rest of the ring processing runs in the same kthread without bottom halves disabled, and can fault normally. Tested-and-acked-by: Michael S. Tsirkin Signed-off-by: Linus Torvalds --- drivers/vdpa/vdpa_sim/vdpa_sim_blk.c | 9 ++++++++- drivers/vdpa/vdpa_sim/vdpa_sim_net.c | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c index 76dd5b0828d7..4d3e36fb59d5 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -343,11 +344,17 @@ static void vdpasim_blk_work(struct vdpasim *vdpasim) continue; while (vdpasim_blk_handle_req(vdpasim, vq)) { + bool notify; + /* Make sure used is visible before rasing the interrupt. */ smp_wmb(); local_bh_disable(); - if (vringh_need_notify_iotlb(&vq->vring) > 0) + /* The ring may be in user memory, and we can't take faults here */ + pagefault_disable(); + notify = vringh_need_notify_iotlb(&vq->vring) > 0; + pagefault_enable(); + if (notify) vringh_notify(&vq->vring); local_bh_enable(); diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim_net.c b/drivers/vdpa/vdpa_sim/vdpa_sim_net.c index a6514b5ccd86..543a58abcdb8 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim_net.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim_net.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,8 @@ static struct vdpasim_net *sim_to_net(struct vdpasim *vdpasim) static void vdpasim_net_complete(struct vdpasim_virtqueue *vq, size_t len) { + bool notify; + /* Make sure data is wrote before advancing index */ smp_wmb(); @@ -77,7 +80,11 @@ static void vdpasim_net_complete(struct vdpasim_virtqueue *vq, size_t len) smp_wmb(); local_bh_disable(); - if (vringh_need_notify_iotlb(&vq->vring) > 0) + /* The ring may be in user memory, and we can't take faults here */ + pagefault_disable(); + notify = vringh_need_notify_iotlb(&vq->vring) > 0; + pagefault_enable(); + if (notify) vringh_notify(&vq->vring); local_bh_enable(); } From 95a214f31a3b1ef40034f1e1cb3b9d758a374f01 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:24:08 -1000 Subject: [PATCH 21/48] vhost: vringh: use get_user()/put_user() for IOTLB 16-bit accesses getu16_iotlb() and putu16_iotlb() use __get_user() and __put_user() when the IOTLB maps to user virtual addresses. Nothing nearby checks the translated address: that relies on the mapping having been validated against the user's VMAs when it was set up. Use get_user() and put_user(), which do the same single, untorn access and also check the address. The only atomic-context use, the vdpa simulators' notification check, now disables page faults around it, so the might_fault() in the checked accessors stays quiet there. This is part of getting rid of __get_user() and __put_user() entirely. Tested-and-acked-by: Michael S. Tsirkin Fixes: 42823a871fd4 ("vringh: support VA with iotlb") Signed-off-by: Linus Torvalds --- drivers/vhost/vringh.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/vhost/vringh.c b/drivers/vhost/vringh.c index 9066f9f12ba1..74836eb60c7a 100644 --- a/drivers/vhost/vringh.c +++ b/drivers/vhost/vringh.c @@ -1213,7 +1213,7 @@ static inline int getu16_iotlb(const struct vringh *vrh, return ret; if (vrh->use_va) { - ret = __get_user(tmp, (__virtio16 __user *)ivec.iov.iovec[0].iov_base); + ret = get_user(tmp, (__virtio16 __user *)ivec.iov.iovec[0].iov_base); if (ret) return ret; } else { @@ -1251,7 +1251,7 @@ static inline int putu16_iotlb(const struct vringh *vrh, tmp = cpu_to_vringh16(vrh, val); if (vrh->use_va) { - ret = __put_user(tmp, (__virtio16 __user *)ivec.iov.iovec[0].iov_base); + ret = put_user(tmp, (__virtio16 __user *)ivec.iov.iovec[0].iov_base); if (ret) return ret; } else { From 738989053b0a5671fcd33ead27d689e973fb89f4 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:24:11 -1000 Subject: [PATCH 22/48] xen: use get_user()/put_user() in gntdev and privcmd gntdev_copy() updates per-segment status words with __get_user() and __put_user(). privcmd writes back mmap batch errors and the resource frame count with __put_user(). The pointers were validated earlier, away from the accesses: by a copy_from_user() of the enclosing structure, or by an access_ok() on the whole array before a callback walks it. Each access enables and disables user access on its own anyway, so the separation saves nothing. Use get_user() and put_user(). This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- drivers/xen/gntdev.c | 4 ++-- drivers/xen/privcmd.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 1dcc4675580e..4ebafde0d296 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -849,13 +849,13 @@ static int gntdev_copy(struct gntdev_copy_batch *batch) if (status == GNTST_okay) continue; - if (__get_user(old_status, batch->status[i])) + if (get_user(old_status, batch->status[i])) return -EFAULT; if (old_status != GNTST_okay) continue; - if (__put_user(status, batch->status[i])) + if (put_user(status, batch->status[i])) return -EFAULT; } diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index 7cfc28f1bb86..18a923a839ff 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c @@ -411,12 +411,12 @@ static int mmap_return_error(int err, struct mmap_batch_state *st) gfn |= (err == -ENOENT) ? PRIVCMD_MMAPBATCH_PAGED_ERROR : PRIVCMD_MMAPBATCH_MFN_ERROR; - return __put_user(gfn, st->user_gfn++); + return put_user(gfn, st->user_gfn++); } else st->user_gfn++; } else { /* st->version == 2 */ if (err) - return __put_user(err, st->user_err++); + return put_user(err, st->user_err++); else st->user_err++; } @@ -777,7 +777,7 @@ static long privcmd_ioctl_mmap_resource(struct file *file, rc = HYPERVISOR_memory_op(XENMEM_acquire_resource, &xdata); if (rc) return rc; - return __put_user(xdata.nr_frames, &udata->num); + return put_user(xdata.nr_frames, &udata->num); } mmap_write_lock(mm); From 03c19e449ac6e12a88ea4866331f2404668e22cc Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:24:15 -1000 Subject: [PATCH 23/48] drm/i915/gem: Don't fault on presumed_offset write-back The execbuffer relocation fast path writes each updated presumed_offset back to the user's relocation array with __put_user(), ignoring any failure. It does that while holding the objects' dma_resv locks, and with page faults enabled unless the relocation cache happens to have an atomic mapping active. So if that write faults, for example on a copy-on-write page after a fork(), the fault handler takes mmap_lock under dma_resv. That is the opposite of the order the i915 fault handler uses, which takes the object lock under mmap_lock. The comment above it claimed this could not happen, because "we did previously check if the relocations were writable (access_ok)". access_ok() is purely a range check. The comment dates from when this was access_ok(VERIFY_WRITE), and that only ever made a difference on the original i386, whose write-protect bit did not stop supervisor writes. So nothing has made sure the array was writable for decades. Do the write with page faults disabled, and if it fails, fall back to the slow path exactly as we already do when reading the relocations fails part-way through. We cannot just ignore the failure: the object has already been relocated, and a stale presumed_offset could later match again and skip a relocation that is needed. The slow path prefaults and retries. If the write still fails, it copies the relocations and marks every presumed_offset invalid with faults allowed and no locks held, which breaks copy-on-write as needed. One user-visible change: a relocation array in genuinely read-only memory used to get through the fast path, silently losing the presumed_offset updates. It now gets -EFAULT from the slow path, as it already did whenever anything else sent execbuffer there. The uapi has the kernel updating presumed_offset, so the array is expected to be writable. Signed-off-by: Linus Torvalds --- .../gpu/drm/i915/gem/i915_gem_execbuffer.c | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c index d5aadced7da0..5fb17c68bbf1 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c @@ -1522,8 +1522,9 @@ static int eb_relocate_vma(struct i915_execbuffer *eb, struct eb_vma *ev) /* * We must check that the entire relocation array is safe - * to read. However, if the array is not writable the user loses - * the updated relocation values. + * to read. Whether it is also writable only shows when we + * write back the presumed offsets below, and if that fails we + * fall back to the slow path. */ if (unlikely(!access_ok(urelocs, remain * sizeof(*urelocs)))) return -EFAULT; @@ -1553,6 +1554,7 @@ static int eb_relocate_vma(struct i915_execbuffer *eb, struct eb_vma *ev) remain -= count; do { u64 offset = eb_relocate_entry(eb, ev, r); + int err; if (likely(offset == 0)) continue; @@ -1562,28 +1564,32 @@ static int eb_relocate_vma(struct i915_execbuffer *eb, struct eb_vma *ev) goto out; } /* - * Note that reporting an error now - * leaves everything in an inconsistent - * state as we have *already* changed - * the relocation value inside the - * object. As we have not changed the - * reloc.presumed_offset or will not - * change the execobject.offset, on the - * call we may not rewrite the value - * inside the object, leaving it - * dangling and causing a GPU hang. Unless - * userspace dynamically rebuilds the - * relocations on each execbuf rather than - * presume a static tree. + * Write back the presumed offset. We hold the + * objects' dma_resv locks here, so this must not + * take a page fault: that would take mmap_lock + * under dma_resv, the opposite of the order our + * own fault handler uses. * - * We did previously check if the relocations - * were writable (access_ok), an error now - * would be a strange race with mprotect, - * having already demonstrated that we - * can read from this userspace address. + * If the write fails (the page is read-only, + * copy-on-write, or gone since we read it), we + * must not just carry on. We have already changed + * the relocation value inside the object, and a + * stale presumed_offset could later match again + * and make us skip a relocation that is needed, + * hanging the GPU. So fall back to the slow path, + * as for a failed read: it retries after + * prefaulting and, failing that, marks every + * presumed_offset invalid with faults allowed and + * no locks held. */ offset = gen8_canonical_addr(offset & ~UPDATE); - __put_user(offset, &urelocs[r - stack].presumed_offset); + pagefault_disable(); + err = put_user(offset, &urelocs[r - stack].presumed_offset); + pagefault_enable(); + if (unlikely(err)) { + remain = -EFAULT; + goto out; + } } while (r++, --count); urelocs += ARRAY_SIZE(stack); } while (remain); From 3a609ff2f12652a23846180e16d96017b84c5d80 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:24:18 -1000 Subject: [PATCH 24/48] drm/i915/gem: Use get_user()/put_user() where no locks are held execbuffer prefaults the relocation array and reads timeline fence values one element at a time with __get_user(), after checking each array with access_ok() up front. Each of those accesses enables and disables user access on its own, so the up-front check saves nothing. Both run with no object locks held: the prefault between dropping and retaking the ww locks, the fence parsing before the ww context exists. Use get_user(). The access_ok() checks stay, since they also decide how a bad array fails. The gem mman selftests use the unchecked variants on addresses they got from their own mmaps, with no check at all. Convert those too. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c | 6 +++--- drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c index 5fb17c68bbf1..8ed5aa3beae3 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c @@ -1641,11 +1641,11 @@ static int check_relocations(const struct drm_i915_gem_exec_object2 *entry) end = addr + size; for (; addr < end; addr += PAGE_SIZE) { - int err = __get_user(c, addr); + int err = get_user(c, addr); if (err) return err; } - return __get_user(c, end - 1); + return get_user(c, end - 1); } static int eb_copy_relocations(const struct i915_execbuffer *eb) @@ -2843,7 +2843,7 @@ add_timeline_fence_array(struct i915_execbuffer *eb, if (user_fence.flags & __I915_EXEC_FENCE_UNKNOWN_FLAGS) return -EINVAL; - if (__get_user(point, user_values++)) + if (get_user(point, user_values++)) return -EFAULT; syncobj = drm_syncobj_find(eb->file, user_fence.handle); diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c index d01acfb7d93d..e6748856a0a8 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c @@ -1465,7 +1465,7 @@ static int __igt_mmap_access(struct drm_i915_private *i915, return addr; ptr = (unsigned long __user *)addr; - err = __put_user(A, ptr); + err = put_user(A, ptr); if (err) { pr_err("%s(%s): failed to write into user mmap\n", obj->mm.region->name, repr_mmap_type(type)); @@ -1490,7 +1490,7 @@ static int __igt_mmap_access(struct drm_i915_private *i915, intel_gt_flush_ggtt_writes(to_gt(i915)); - err = __get_user(y, ptr); + err = get_user(y, ptr); if (err) { pr_err("%s(%s): failed to read from user mmap\n", obj->mm.region->name, repr_mmap_type(type)); @@ -1738,12 +1738,12 @@ static int prefault_range(u64 start, u64 len) end = addr + len; for (; addr < end; addr += PAGE_SIZE) { - err = __get_user(c, addr); + err = get_user(c, addr); if (err) return err; } - return __get_user(c, end - 1); + return get_user(c, end - 1); } static int __igt_mmap_revoke(struct drm_i915_private *i915, From 0c5db8acc6e99627a645a395f96b921d5558bf32 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:24:20 -1000 Subject: [PATCH 25/48] char: nwflash: use get_user() in write_block() write_block() reads the user buffer one byte at a time with __get_user() while programming and then verifying the flash, relying on the access_ok() done in flash_write(). Each __get_user() enables and disables user access on its own anyway, so the separation saves nothing. Use get_user(). This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- drivers/char/nwflash.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c index 9f52f0306ef7..c3df2b032e29 100644 --- a/drivers/char/nwflash.c +++ b/drivers/char/nwflash.c @@ -422,7 +422,7 @@ static int write_block(unsigned long p, const char __user *buf, int count) for (offset = 0; offset < count; offset++, pWritePtr++) { uAddress = (unsigned int) pWritePtr; uAddress &= 0xFFFFFFFC; - if (__get_user(c2, buf + offset)) + if (get_user(c2, buf + offset)) return -EFAULT; WriteRetry: @@ -533,7 +533,7 @@ static int write_block(unsigned long p, const char __user *buf, int count) for (offset = 0; offset < count; offset++) { char c, c1; - if (__get_user(c, buf)) + if (get_user(c, buf)) return -EFAULT; buf++; if ((c1 = *pWritePtr++) != c) { From 7170bd79ca52afbe1e0258f3080401385a6b3c49 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:24:22 -1000 Subject: [PATCH 26/48] macintosh: ans-lcd: use get_user() for reading user bytes anslcd_write() checks the user buffer with access_ok() and then reads it one byte at a time with __get_user(), writing each byte to the LCD in between. The ANSLCD_SENDCTRL ioctl does the same with the ioctl argument, but with no access_ok() at all, so on architectures where __get_user() does not check the address it will read from any address it is given. Use get_user() in both places. The ignored return values stay as they were: a fault still reads as zero, which ends the SENDCTRL loop. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- drivers/macintosh/ans-lcd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/macintosh/ans-lcd.c b/drivers/macintosh/ans-lcd.c index fa904b24a600..673ff672af8a 100644 --- a/drivers/macintosh/ans-lcd.c +++ b/drivers/macintosh/ans-lcd.c @@ -71,7 +71,7 @@ anslcd_write( struct file * file, const char __user * buf, for ( i = *ppos; count > 0; ++i, ++p, --count ) { char c; - __get_user(c, p); + get_user(c, p); anslcd_write_byte_data( c ); } mutex_unlock(&anslcd_mutex); @@ -102,10 +102,10 @@ anslcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case ANSLCD_SENDCTRL: temp = (char __user *) arg; - __get_user(ch, temp); + get_user(ch, temp); for (; ch; temp++) { /* FIXME: This is ugly, but should work, as a \0 byte is not a valid command code */ anslcd_write_byte_ctrl ( ch ); - __get_user(ch, temp); + get_user(ch, temp); } break; case ANSLCD_SETSHORTDELAY: From 5d64749b7df20ec3f0820fc94eefecfbf10f197e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:24:25 -1000 Subject: [PATCH 27/48] x86/sev: Use explicit user-or-kernel accessors for #VC memory operands vc_read_mem() and vc_write_mem() access the memory operand of an emulated MOVS or INS/OUTS. That address may be a kernel address or a user one, and the access must be a single MOV rather than a string instruction, since the operand can itself be MMIO and take a nested #VC. The code used __get_user() and __put_user() for this, on the grounds that they do no pointer checks. That is a side effect of how x86 happens to implement them, not something they are for, and they are going away. Replace them with two local helpers that say what this is: a "user or kernel" access with fault handling. They enable user access by hand rather than through user_access_begin(), whose range check would reject a kernel address, and use unsafe_get_user() and unsafe_put_user() for the single MOV and its exception table entry. The read side uses __uaccess_begin_nospec(), keeping the lfence that __get_user() used to provide. The old comment also claimed this always runs in atomic context, so the page fault handler would not try to handle a fault. That holds when the exception comes from kernel mode, where the handler runs as an NMI. It does not hold when it comes from user mode, where the handler only has interrupts disabled and so is not in_atomic(). A user-mode "rep insb" into a not-yet-faulted buffer therefore went into do_user_addr_fault() with interrupts off, and was only stopped by its legacy WARN_ON_ONCE() on a clear IF flag. The helpers now disable page faults explicitly, so the fault is refused up front and forwarded to the instruction as a #PF, which is what was intended. Signed-off-by: Linus Torvalds --- arch/x86/coco/sev/vc-handle.c | 217 ++++++++++++++++------------------ 1 file changed, 100 insertions(+), 117 deletions(-) diff --git a/arch/x86/coco/sev/vc-handle.c b/arch/x86/coco/sev/vc-handle.c index 96b62b49b2b5..f25d302efceb 100644 --- a/arch/x86/coco/sev/vc-handle.c +++ b/arch/x86/coco/sev/vc-handle.c @@ -194,75 +194,113 @@ static enum es_result vc_decode_insn(struct es_em_ctxt *ctxt) return __vc_decode_kern_insn(ctxt); } +/* + * The memory operand of an emulated string instruction can be a user or a + * kernel address. MOVS to and from MMIO is only emulated for the kernel, + * but the kernel may be copying to or from user memory at the time, and + * INS/OUTS are emulated for both. So these are "user or kernel" accesses + * with fault handling: a single plain MOV of the given size, with an + * exception table entry to catch a fault. User access is enabled by hand + * so that a user address can be reached, without the range check of + * user_access_begin(), which would reject a kernel address. + * + * It must be a single MOV and not a string instruction, because the address + * can itself be MMIO. That takes a nested #VC, and a nested MOVS would nest + * forever. + * + * Page faults are disabled explicitly. A #VC from kernel mode runs as an NMI + * and is atomic anyway, but one from user mode only has interrupts disabled, + * which the page fault handler does not treat as atomic. A fault here is + * not handled: it is reported back as a #PF for the instruction that took the + * #VC, and gets handled there. + * + * The caller has already checked that size is 1, 2, 4 or 8. + */ +static bool vc_get_mem(void *buf, const void *addr, size_t size) +{ + union { u8 d1; u16 d2; u32 d4; u64 d8; } val; + + guard(pagefault)(); + __uaccess_begin_nospec(); + switch (size) { + case 1: + unsafe_get_user(val.d1, (const u8 __user __force *)addr, efault); + break; + case 2: + unsafe_get_user(val.d2, (const u16 __user __force *)addr, efault); + break; + case 4: + unsafe_get_user(val.d4, (const u32 __user __force *)addr, efault); + break; + case 8: + unsafe_get_user(val.d8, (const u64 __user __force *)addr, efault); + break; + } + __uaccess_end(); + + memcpy(buf, &val, size); + return true; + +efault: + __uaccess_end(); + return false; +} + +static bool vc_put_mem(void *addr, const void *buf, size_t size) +{ + union { u8 d1; u16 d2; u32 d4; u64 d8; } val; + + memcpy(&val, buf, size); + + guard(pagefault)(); + __uaccess_begin(); + switch (size) { + case 1: + unsafe_put_user(val.d1, (u8 __user __force *)addr, efault); + break; + case 2: + unsafe_put_user(val.d2, (u16 __user __force *)addr, efault); + break; + case 4: + unsafe_put_user(val.d4, (u32 __user __force *)addr, efault); + break; + case 8: + unsafe_put_user(val.d8, (u64 __user __force *)addr, efault); + break; + } + __uaccess_end(); + return true; + +efault: + __uaccess_end(); + return false; +} + +static bool vc_mem_size_ok(size_t size) +{ + switch (size) { + case 1: + case 2: + case 4: + case 8: + return true; + } + return false; +} + static enum es_result vc_write_mem(struct es_em_ctxt *ctxt, char *dst, char *buf, size_t size) { unsigned long error_code = X86_PF_PROT | X86_PF_WRITE; - /* - * This function uses __put_user() independent of whether kernel or user - * memory is accessed. This works fine because __put_user() does no - * sanity checks of the pointer being accessed. All that it does is - * to report when the access failed. - * - * Also, this function runs in atomic context, so __put_user() is not - * allowed to sleep. The page-fault handler detects that it is running - * in atomic context and will not try to take mmap_sem and handle the - * fault, so additional pagefault_enable()/disable() calls are not - * needed. - * - * The access can't be done via copy_to_user() here because - * vc_write_mem() must not use string instructions to access unsafe - * memory. The reason is that MOVS is emulated by the #VC handler by - * splitting the move up into a read and a write and taking a nested #VC - * exception on whatever of them is the MMIO access. Using string - * instructions here would cause infinite nesting. - */ - switch (size) { - case 1: { - u8 d1; - u8 __user *target = (u8 __user *)dst; - - memcpy(&d1, buf, 1); - if (__put_user(d1, target)) - goto fault; - break; - } - case 2: { - u16 d2; - u16 __user *target = (u16 __user *)dst; - - memcpy(&d2, buf, 2); - if (__put_user(d2, target)) - goto fault; - break; - } - case 4: { - u32 d4; - u32 __user *target = (u32 __user *)dst; - - memcpy(&d4, buf, 4); - if (__put_user(d4, target)) - goto fault; - break; - } - case 8: { - u64 d8; - u64 __user *target = (u64 __user *)dst; - - memcpy(&d8, buf, 8); - if (__put_user(d8, target)) - goto fault; - break; - } - default: + if (!vc_mem_size_ok(size)) { WARN_ONCE(1, "%s: Invalid size: %zu\n", __func__, size); return ES_UNSUPPORTED; } - return ES_OK; + if (vc_put_mem(dst, buf, size)) + return ES_OK; -fault: if (user_mode(ctxt->regs)) error_code |= X86_PF_USER; @@ -278,69 +316,14 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt, { unsigned long error_code = X86_PF_PROT; - /* - * This function uses __get_user() independent of whether kernel or user - * memory is accessed. This works fine because __get_user() does no - * sanity checks of the pointer being accessed. All that it does is - * to report when the access failed. - * - * Also, this function runs in atomic context, so __get_user() is not - * allowed to sleep. The page-fault handler detects that it is running - * in atomic context and will not try to take mmap_sem and handle the - * fault, so additional pagefault_enable()/disable() calls are not - * needed. - * - * The access can't be done via copy_from_user() here because - * vc_read_mem() must not use string instructions to access unsafe - * memory. The reason is that MOVS is emulated by the #VC handler by - * splitting the move up into a read and a write and taking a nested #VC - * exception on whatever of them is the MMIO access. Using string - * instructions here would cause infinite nesting. - */ - switch (size) { - case 1: { - u8 d1; - u8 __user *s = (u8 __user *)src; - - if (__get_user(d1, s)) - goto fault; - memcpy(buf, &d1, 1); - break; - } - case 2: { - u16 d2; - u16 __user *s = (u16 __user *)src; - - if (__get_user(d2, s)) - goto fault; - memcpy(buf, &d2, 2); - break; - } - case 4: { - u32 d4; - u32 __user *s = (u32 __user *)src; - - if (__get_user(d4, s)) - goto fault; - memcpy(buf, &d4, 4); - break; - } - case 8: { - u64 d8; - u64 __user *s = (u64 __user *)src; - if (__get_user(d8, s)) - goto fault; - memcpy(buf, &d8, 8); - break; - } - default: + if (!vc_mem_size_ok(size)) { WARN_ONCE(1, "%s: Invalid size: %zu\n", __func__, size); return ES_UNSUPPORTED; } - return ES_OK; + if (vc_get_mem(buf, src, size)) + return ES_OK; -fault: if (user_mode(ctxt->regs)) error_code |= X86_PF_USER; From f748af39e33565f8514d609528912776c9d93511 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:24:27 -1000 Subject: [PATCH 28/48] x86/uaccess: Remove __get_user() and __put_user() Nothing that builds on x86 uses __get_user() or __put_user() any more. They were the "unchecked" user accessors: no range check, on the assumption that the caller had already done an access_ok(). That stopped buying anything once every user access had to be enabled and disabled on its own, and on x86 __get_user() had become slower than get_user(), since it does an lfence where get_user() checks the address with a couple of plain instructions. Several of the last users were also relying on them for things they were never meant to do: accessing kernel addresses, or staying quiet in atomic context. Remove the macros and the out-of-line __{get,put}_user_nocheck_N helpers behind them, so that new users cannot appear. Other architectures still provide their own versions. UML gets its accessors from asm-generic and is not affected. arm64's __get_user() and __put_user() have always been the same as get_user() and put_user(), so there is no hurry there. Signed-off-by: Linus Torvalds --- arch/x86/include/asm/uaccess.h | 60 ++-------------------------------- arch/x86/lib/getuser.S | 56 +------------------------------ arch/x86/lib/putuser.S | 60 ++++------------------------------ 3 files changed, 10 insertions(+), 166 deletions(-) diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h index 3a0dd3c2b233..f4700ad6b5ec 100644 --- a/arch/x86/include/asm/uaccess.h +++ b/arch/x86/include/asm/uaccess.h @@ -28,10 +28,6 @@ extern int __get_user_1(void); extern int __get_user_2(void); extern int __get_user_4(void); extern int __get_user_8(void); -extern int __get_user_nocheck_1(void); -extern int __get_user_nocheck_2(void); -extern int __get_user_nocheck_4(void); -extern int __get_user_nocheck_8(void); extern int __get_user_bad(void); #define __uaccess_begin() stac() @@ -56,10 +52,9 @@ extern int __get_user_bad(void); __builtin_choose_expr(sizeof(x)<=sizeof(type),(unsigned type)0,not) /* - * This is used for both get_user() and __get_user() to expand to - * the proper special function call that has odd calling conventions - * due to returning both a value and an error, and that depends on - * the size of the pointer passed in. + * This is used by get_user() to expand to the proper special function + * call that has odd calling conventions due to returning both a value + * and an error, and that depends on the size of the pointer passed in. * * Careful: we have to cast the result to the type of the pointer * for sign reasons. @@ -107,29 +102,6 @@ extern int __get_user_bad(void); */ #define get_user(x,ptr) ({ might_fault(); do_get_user_call(get_user,x,ptr); }) -/** - * __get_user - Get a simple variable from user space, with less checking. - * @x: Variable to store result. - * @ptr: Source address, in user space. - * - * Context: User context only. This function may sleep if pagefaults are - * enabled. - * - * This macro copies a single simple variable from user space to kernel - * space. It supports simple types like char and int, but not larger - * data types like structures or arrays. - * - * @ptr must have pointer-to-simple-variable type, and the result of - * dereferencing @ptr must be assignable to @x without a cast. - * - * Caller must check the pointer with access_ok() before calling this - * function. - * - * Return: zero on success, or -EFAULT on error. - * On error, the variable @x is set to zero. - */ -#define __get_user(x,ptr) do_get_user_call(get_user_nocheck,x,ptr) - #ifdef CONFIG_X86_32 #define __put_user_goto_u64(x, addr, label) \ @@ -156,10 +128,6 @@ extern void __put_user_1(void); extern void __put_user_2(void); extern void __put_user_4(void); extern void __put_user_8(void); -extern void __put_user_nocheck_1(void); -extern void __put_user_nocheck_2(void); -extern void __put_user_nocheck_4(void); -extern void __put_user_nocheck_8(void); /* * ptr must be evaluated and assigned to the temporary __ptr_pu before @@ -207,28 +175,6 @@ extern void __put_user_nocheck_8(void); */ #define put_user(x, ptr) ({ might_fault(); do_put_user_call(put_user,x,ptr); }) -/** - * __put_user - Write a simple value into user space, with less checking. - * @x: Value to copy to user space. - * @ptr: Destination address, in user space. - * - * Context: User context only. This function may sleep if pagefaults are - * enabled. - * - * This macro copies a single simple value from kernel space to user - * space. It supports simple types like char and int, but not larger - * data types like structures or arrays. - * - * @ptr must have pointer-to-simple-variable type, and @x must be assignable - * to the result of dereferencing @ptr. - * - * Caller must check the pointer with access_ok() before calling this - * function. - * - * Return: zero on success, or -EFAULT on error. - */ -#define __put_user(x, ptr) do_put_user_call(put_user_nocheck,x,ptr) - #define __put_user_size(x, ptr, size, label) \ do { \ __typeof__(*(ptr)) __x = (x); /* eval x once */ \ diff --git a/arch/x86/lib/getuser.S b/arch/x86/lib/getuser.S index 9d5654b8a72a..7c79eca08b0a 100644 --- a/arch/x86/lib/getuser.S +++ b/arch/x86/lib/getuser.S @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * __get_user functions. + * The out-of-line helpers for get_user(). * * (C) Copyright 1998 Linus Torvalds * (C) Copyright 2005 Andi Kleen @@ -37,8 +37,6 @@ #include #include -#define ASM_BARRIER_NOSPEC ALTERNATIVE "", "lfence", X86_FEATURE_LFENCE_RDTSC - .macro check_range size:req .if IS_ENABLED(CONFIG_X86_64) RUNTIME_CONST_PTR USER_PTR_MAX, rdx @@ -111,58 +109,6 @@ SYM_FUNC_START(__get_user_8) SYM_FUNC_END(__get_user_8) EXPORT_SYMBOL(__get_user_8) -/* .. and the same for __get_user, just without the range checks */ -SYM_FUNC_START(__get_user_nocheck_1) - ANNOTATE_NOENDBR - ASM_STAC - ASM_BARRIER_NOSPEC - UACCESS movzbl (%_ASM_AX),%edx - xor %eax,%eax - ASM_CLAC - RET -SYM_FUNC_END(__get_user_nocheck_1) -EXPORT_SYMBOL(__get_user_nocheck_1) - -SYM_FUNC_START(__get_user_nocheck_2) - ANNOTATE_NOENDBR - ASM_STAC - ASM_BARRIER_NOSPEC - UACCESS movzwl (%_ASM_AX),%edx - xor %eax,%eax - ASM_CLAC - RET -SYM_FUNC_END(__get_user_nocheck_2) -EXPORT_SYMBOL(__get_user_nocheck_2) - -SYM_FUNC_START(__get_user_nocheck_4) - ANNOTATE_NOENDBR - ASM_STAC - ASM_BARRIER_NOSPEC - UACCESS movl (%_ASM_AX),%edx - xor %eax,%eax - ASM_CLAC - RET -SYM_FUNC_END(__get_user_nocheck_4) -EXPORT_SYMBOL(__get_user_nocheck_4) - -SYM_FUNC_START(__get_user_nocheck_8) - ANNOTATE_NOENDBR - ASM_STAC - ASM_BARRIER_NOSPEC -#ifdef CONFIG_X86_64 - UACCESS movq (%_ASM_AX),%rdx -#else - xor %ecx,%ecx - UACCESS movl (%_ASM_AX),%edx - UACCESS movl 4(%_ASM_AX),%ecx -#endif - xor %eax,%eax - ASM_CLAC - RET -SYM_FUNC_END(__get_user_nocheck_8) -EXPORT_SYMBOL(__get_user_nocheck_8) - - SYM_CODE_START_LOCAL(__get_user_handle_exception) ASM_CLAC .Lbad_get_user: diff --git a/arch/x86/lib/putuser.S b/arch/x86/lib/putuser.S index 46d9e9b98a61..9ad19f907485 100644 --- a/arch/x86/lib/putuser.S +++ b/arch/x86/lib/putuser.S @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * __put_user functions. + * The out-of-line helpers for put_user(). * * (C) Copyright 2005 Linus Torvalds * (C) Copyright 2005 Andi Kleen @@ -56,65 +56,35 @@ SYM_FUNC_START(__put_user_1) SYM_FUNC_END(__put_user_1) EXPORT_SYMBOL(__put_user_1) -SYM_FUNC_START(__put_user_nocheck_1) - ANNOTATE_NOENDBR - ASM_STAC -2: movb %al,(%_ASM_CX) - xor %ecx,%ecx - ASM_CLAC - RET -SYM_FUNC_END(__put_user_nocheck_1) -EXPORT_SYMBOL(__put_user_nocheck_1) - SYM_FUNC_START(__put_user_2) ANNOTATE_NOENDBR check_range size=2 ASM_STAC -3: movw %ax,(%_ASM_CX) +2: movw %ax,(%_ASM_CX) xor %ecx,%ecx ASM_CLAC RET SYM_FUNC_END(__put_user_2) EXPORT_SYMBOL(__put_user_2) -SYM_FUNC_START(__put_user_nocheck_2) - ANNOTATE_NOENDBR - ASM_STAC -4: movw %ax,(%_ASM_CX) - xor %ecx,%ecx - ASM_CLAC - RET -SYM_FUNC_END(__put_user_nocheck_2) -EXPORT_SYMBOL(__put_user_nocheck_2) - SYM_FUNC_START(__put_user_4) ANNOTATE_NOENDBR check_range size=4 ASM_STAC -5: movl %eax,(%_ASM_CX) +3: movl %eax,(%_ASM_CX) xor %ecx,%ecx ASM_CLAC RET SYM_FUNC_END(__put_user_4) EXPORT_SYMBOL(__put_user_4) -SYM_FUNC_START(__put_user_nocheck_4) - ANNOTATE_NOENDBR - ASM_STAC -6: movl %eax,(%_ASM_CX) - xor %ecx,%ecx - ASM_CLAC - RET -SYM_FUNC_END(__put_user_nocheck_4) -EXPORT_SYMBOL(__put_user_nocheck_4) - SYM_FUNC_START(__put_user_8) ANNOTATE_NOENDBR check_range size=8 ASM_STAC -7: mov %_ASM_AX,(%_ASM_CX) +4: mov %_ASM_AX,(%_ASM_CX) #ifdef CONFIG_X86_32 -8: movl %edx,4(%_ASM_CX) +5: movl %edx,4(%_ASM_CX) #endif xor %ecx,%ecx ASM_CLAC @@ -122,19 +92,6 @@ SYM_FUNC_START(__put_user_8) SYM_FUNC_END(__put_user_8) EXPORT_SYMBOL(__put_user_8) -SYM_FUNC_START(__put_user_nocheck_8) - ANNOTATE_NOENDBR - ASM_STAC -9: mov %_ASM_AX,(%_ASM_CX) -#ifdef CONFIG_X86_32 -10: movl %edx,4(%_ASM_CX) -#endif - xor %ecx,%ecx - ASM_CLAC - RET -SYM_FUNC_END(__put_user_nocheck_8) -EXPORT_SYMBOL(__put_user_nocheck_8) - SYM_CODE_START_LOCAL(__put_user_handle_exception) ASM_CLAC .Lbad_put_user: @@ -146,11 +103,6 @@ SYM_CODE_END(__put_user_handle_exception) _ASM_EXTABLE_UA(2b, __put_user_handle_exception) _ASM_EXTABLE_UA(3b, __put_user_handle_exception) _ASM_EXTABLE_UA(4b, __put_user_handle_exception) - _ASM_EXTABLE_UA(5b, __put_user_handle_exception) - _ASM_EXTABLE_UA(6b, __put_user_handle_exception) - _ASM_EXTABLE_UA(7b, __put_user_handle_exception) - _ASM_EXTABLE_UA(9b, __put_user_handle_exception) #ifdef CONFIG_X86_32 - _ASM_EXTABLE_UA(8b, __put_user_handle_exception) - _ASM_EXTABLE_UA(10b, __put_user_handle_exception) + _ASM_EXTABLE_UA(5b, __put_user_handle_exception) #endif From ef9a9b616a6f2145fa85d5cb4e58441091391b26 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 08:26:44 -1000 Subject: [PATCH 29/48] alpha: use scoped user access in osf_sigaction() osf_sigaction() does one access_ok() on each user structure and then reads or writes its fields with __get_user() or __put_user(), the same pattern as the generic old-style sigaction(). Convert it the same way, to scoped_user_{read,write}_access() with unsafe_{get,put}_user(). alpha has no user_access_begin(), so the scope is the same access_ok() followed by the same plain accesses, and the generated code does not change. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/alpha/kernel/signal.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c index ce40a49b8496..5077cdbf78ab 100644 --- a/arch/alpha/kernel/signal.c +++ b/arch/alpha/kernel/signal.c @@ -73,11 +73,11 @@ SYSCALL_DEFINE3(osf_sigaction, int, sig, if (act) { old_sigset_t mask; - if (!access_ok(act, sizeof(*act)) || - __get_user(new_ka.sa.sa_handler, &act->sa_handler) || - __get_user(new_ka.sa.sa_flags, &act->sa_flags) || - __get_user(mask, &act->sa_mask)) - return -EFAULT; + scoped_user_read_access(act, Efault) { + unsafe_get_user(new_ka.sa.sa_handler, &act->sa_handler, Efault); + unsafe_get_user(new_ka.sa.sa_flags, &act->sa_flags, Efault); + unsafe_get_user(mask, &act->sa_mask, Efault); + } siginitset(&new_ka.sa.sa_mask, mask); new_ka.ka_restorer = NULL; } @@ -85,14 +85,17 @@ SYSCALL_DEFINE3(osf_sigaction, int, sig, ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); if (!ret && oact) { - if (!access_ok(oact, sizeof(*oact)) || - __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || - __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || - __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) - return -EFAULT; + scoped_user_write_access(oact, Efault) { + unsafe_put_user(old_ka.sa.sa_handler, &oact->sa_handler, Efault); + unsafe_put_user(old_ka.sa.sa_flags, &oact->sa_flags, Efault); + unsafe_put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask, Efault); + } } return ret; + +Efault: + return -EFAULT; } SYSCALL_DEFINE5(rt_sigaction, int, sig, const struct sigaction __user *, act, From 9622c1bc291f3119f52d6781e4c8509e74ed3098 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 08:27:23 -1000 Subject: [PATCH 30/48] alpha: use scoped user access in the sigreturn path restore_sigcontext() reads the whole saved register set with __get_user() and __copy_from_user(), OR-ing the errors together. It relies on its callers having done access_ok(): do_sigreturn() on the sigcontext, do_rt_sigreturn() on the enclosing ucontext. Make it do its own check, with a scoped_user_read_access() around unsafe_get_user() and unsafe_copy_from_user(). do_sigreturn() then only needs the same for the signal mask. do_rt_sigreturn() keeps its access_ok(), which its own __copy_from_user() of the mask still needs. A fault part way through the frame now stops the restore there, instead of carrying on with zeroes and still writing the user stack pointer. Either way the sigreturn fails and the task gets SIGSEGV. alpha has no user_access_begin(), so the accesses themselves are the same as before. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/alpha/kernel/signal.c | 84 +++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c index 5077cdbf78ab..fa6f6c8732c3 100644 --- a/arch/alpha/kernel/signal.c +++ b/arch/alpha/kernel/signal.c @@ -161,51 +161,54 @@ restore_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs) { unsigned long usp; struct switch_stack *sw = (struct switch_stack *)regs - 1; - long err = __get_user(regs->pc, &sc->sc_pc); current->restart_block.fn = do_no_restart_syscall; current_thread_info()->status |= TS_SAVED_FP | TS_RESTORE_FP; sw->r26 = (unsigned long) ret_from_sys_call; - err |= __get_user(regs->r0, sc->sc_regs+0); - err |= __get_user(regs->r1, sc->sc_regs+1); - err |= __get_user(regs->r2, sc->sc_regs+2); - err |= __get_user(regs->r3, sc->sc_regs+3); - err |= __get_user(regs->r4, sc->sc_regs+4); - err |= __get_user(regs->r5, sc->sc_regs+5); - err |= __get_user(regs->r6, sc->sc_regs+6); - err |= __get_user(regs->r7, sc->sc_regs+7); - err |= __get_user(regs->r8, sc->sc_regs+8); - err |= __get_user(sw->r9, sc->sc_regs+9); - err |= __get_user(sw->r10, sc->sc_regs+10); - err |= __get_user(sw->r11, sc->sc_regs+11); - err |= __get_user(sw->r12, sc->sc_regs+12); - err |= __get_user(sw->r13, sc->sc_regs+13); - err |= __get_user(sw->r14, sc->sc_regs+14); - err |= __get_user(sw->r15, sc->sc_regs+15); - err |= __get_user(regs->r16, sc->sc_regs+16); - err |= __get_user(regs->r17, sc->sc_regs+17); - err |= __get_user(regs->r18, sc->sc_regs+18); - err |= __get_user(regs->r19, sc->sc_regs+19); - err |= __get_user(regs->r20, sc->sc_regs+20); - err |= __get_user(regs->r21, sc->sc_regs+21); - err |= __get_user(regs->r22, sc->sc_regs+22); - err |= __get_user(regs->r23, sc->sc_regs+23); - err |= __get_user(regs->r24, sc->sc_regs+24); - err |= __get_user(regs->r25, sc->sc_regs+25); - err |= __get_user(regs->r26, sc->sc_regs+26); - err |= __get_user(regs->r27, sc->sc_regs+27); - err |= __get_user(regs->r28, sc->sc_regs+28); - err |= __get_user(regs->gp, sc->sc_regs+29); - err |= __get_user(usp, sc->sc_regs+30); + scoped_user_read_access(sc, Efault) { + unsafe_get_user(regs->pc, &sc->sc_pc, Efault); + unsafe_get_user(regs->r0, sc->sc_regs+0, Efault); + unsafe_get_user(regs->r1, sc->sc_regs+1, Efault); + unsafe_get_user(regs->r2, sc->sc_regs+2, Efault); + unsafe_get_user(regs->r3, sc->sc_regs+3, Efault); + unsafe_get_user(regs->r4, sc->sc_regs+4, Efault); + unsafe_get_user(regs->r5, sc->sc_regs+5, Efault); + unsafe_get_user(regs->r6, sc->sc_regs+6, Efault); + unsafe_get_user(regs->r7, sc->sc_regs+7, Efault); + unsafe_get_user(regs->r8, sc->sc_regs+8, Efault); + unsafe_get_user(sw->r9, sc->sc_regs+9, Efault); + unsafe_get_user(sw->r10, sc->sc_regs+10, Efault); + unsafe_get_user(sw->r11, sc->sc_regs+11, Efault); + unsafe_get_user(sw->r12, sc->sc_regs+12, Efault); + unsafe_get_user(sw->r13, sc->sc_regs+13, Efault); + unsafe_get_user(sw->r14, sc->sc_regs+14, Efault); + unsafe_get_user(sw->r15, sc->sc_regs+15, Efault); + unsafe_get_user(regs->r16, sc->sc_regs+16, Efault); + unsafe_get_user(regs->r17, sc->sc_regs+17, Efault); + unsafe_get_user(regs->r18, sc->sc_regs+18, Efault); + unsafe_get_user(regs->r19, sc->sc_regs+19, Efault); + unsafe_get_user(regs->r20, sc->sc_regs+20, Efault); + unsafe_get_user(regs->r21, sc->sc_regs+21, Efault); + unsafe_get_user(regs->r22, sc->sc_regs+22, Efault); + unsafe_get_user(regs->r23, sc->sc_regs+23, Efault); + unsafe_get_user(regs->r24, sc->sc_regs+24, Efault); + unsafe_get_user(regs->r25, sc->sc_regs+25, Efault); + unsafe_get_user(regs->r26, sc->sc_regs+26, Efault); + unsafe_get_user(regs->r27, sc->sc_regs+27, Efault); + unsafe_get_user(regs->r28, sc->sc_regs+28, Efault); + unsafe_get_user(regs->gp, sc->sc_regs+29, Efault); + unsafe_get_user(usp, sc->sc_regs+30, Efault); + unsafe_copy_from_user(current_thread_info()->fp, + sc->sc_fpregs, 31 * 8, Efault); + unsafe_get_user(current_thread_info()->fp[31], &sc->sc_fpcr, Efault); + } wrusp(usp); + return 0; - err |= __copy_from_user(current_thread_info()->fp, - sc->sc_fpregs, 31 * 8); - err |= __get_user(current_thread_info()->fp[31], &sc->sc_fpcr); - - return err; +Efault: + return -EFAULT; } /* Note that this syscall is also used by setcontext(3) to install @@ -218,11 +221,8 @@ do_sigreturn(struct sigcontext __user *sc) struct pt_regs *regs = current_pt_regs(); sigset_t set; - /* Verify that it's a good sigcontext before using it */ - if (!access_ok(sc, sizeof(*sc))) - goto give_sigsegv; - if (__get_user(set.sig[0], &sc->sc_mask)) - goto give_sigsegv; + scoped_user_read_access(sc, give_sigsegv) + unsafe_get_user(set.sig[0], &sc->sc_mask, give_sigsegv); set_current_blocked(&set); From 872df694064f45c90cb3903034b78a4c9957ca7c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 08:34:26 -1000 Subject: [PATCH 31/48] alpha: use scoped user access when setting up signal frames setup_sigcontext() writes the whole register set with __put_user() and __copy_to_user(), OR-ing the errors together, relying on its callers' access_ok() on the frame. setup_frame() and setup_rt_frame() write the sigreturn trampoline, and setup_rt_frame() the ucontext header, the same way. Do those writes inside scoped_user_write_access() with unsafe_put_user() and unsafe_copy_to_user(). on_sig_stack() is evaluated before the scope, and the instruction cache flush stays outside it. The up-front access_ok() calls stay: setup_rt_frame() still needs its own for the __copy_to_user() of the signal mask. A fault part way through now stops at the first failing write instead of carrying on. Either way the frame setup fails and the task gets SIGSEGV. alpha has no user_access_begin(), so the accesses themselves are the same as before. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/alpha/kernel/signal.c | 138 ++++++++++++++++++++----------------- 1 file changed, 73 insertions(+), 65 deletions(-) diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c index fa6f6c8732c3..9ab266164665 100644 --- a/arch/alpha/kernel/signal.c +++ b/arch/alpha/kernel/signal.c @@ -284,62 +284,66 @@ setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, unsigned long mask, unsigned long sp) { struct switch_stack *sw = (struct switch_stack *)regs - 1; - long err = 0; + long onstack = on_sig_stack((unsigned long)sc); - err |= __put_user(on_sig_stack((unsigned long)sc), &sc->sc_onstack); - err |= __put_user(mask, &sc->sc_mask); - err |= __put_user(regs->pc, &sc->sc_pc); - err |= __put_user(8, &sc->sc_ps); + scoped_user_write_access(sc, Efault) { + unsafe_put_user(onstack, &sc->sc_onstack, Efault); + unsafe_put_user(mask, &sc->sc_mask, Efault); + unsafe_put_user(regs->pc, &sc->sc_pc, Efault); + unsafe_put_user(8, &sc->sc_ps, Efault); - err |= __put_user(regs->r0 , sc->sc_regs+0); - err |= __put_user(regs->r1 , sc->sc_regs+1); - err |= __put_user(regs->r2 , sc->sc_regs+2); - err |= __put_user(regs->r3 , sc->sc_regs+3); - err |= __put_user(regs->r4 , sc->sc_regs+4); - err |= __put_user(regs->r5 , sc->sc_regs+5); - err |= __put_user(regs->r6 , sc->sc_regs+6); - err |= __put_user(regs->r7 , sc->sc_regs+7); - err |= __put_user(regs->r8 , sc->sc_regs+8); - err |= __put_user(sw->r9 , sc->sc_regs+9); - err |= __put_user(sw->r10 , sc->sc_regs+10); - err |= __put_user(sw->r11 , sc->sc_regs+11); - err |= __put_user(sw->r12 , sc->sc_regs+12); - err |= __put_user(sw->r13 , sc->sc_regs+13); - err |= __put_user(sw->r14 , sc->sc_regs+14); - err |= __put_user(sw->r15 , sc->sc_regs+15); - err |= __put_user(regs->r16, sc->sc_regs+16); - err |= __put_user(regs->r17, sc->sc_regs+17); - err |= __put_user(regs->r18, sc->sc_regs+18); - err |= __put_user(regs->r19, sc->sc_regs+19); - err |= __put_user(regs->r20, sc->sc_regs+20); - err |= __put_user(regs->r21, sc->sc_regs+21); - err |= __put_user(regs->r22, sc->sc_regs+22); - err |= __put_user(regs->r23, sc->sc_regs+23); - err |= __put_user(regs->r24, sc->sc_regs+24); - err |= __put_user(regs->r25, sc->sc_regs+25); - err |= __put_user(regs->r26, sc->sc_regs+26); - err |= __put_user(regs->r27, sc->sc_regs+27); - err |= __put_user(regs->r28, sc->sc_regs+28); - err |= __put_user(regs->gp , sc->sc_regs+29); - err |= __put_user(sp, sc->sc_regs+30); - err |= __put_user(0, sc->sc_regs+31); + unsafe_put_user(regs->r0, sc->sc_regs+0, Efault); + unsafe_put_user(regs->r1, sc->sc_regs+1, Efault); + unsafe_put_user(regs->r2, sc->sc_regs+2, Efault); + unsafe_put_user(regs->r3, sc->sc_regs+3, Efault); + unsafe_put_user(regs->r4, sc->sc_regs+4, Efault); + unsafe_put_user(regs->r5, sc->sc_regs+5, Efault); + unsafe_put_user(regs->r6, sc->sc_regs+6, Efault); + unsafe_put_user(regs->r7, sc->sc_regs+7, Efault); + unsafe_put_user(regs->r8, sc->sc_regs+8, Efault); + unsafe_put_user(sw->r9, sc->sc_regs+9, Efault); + unsafe_put_user(sw->r10, sc->sc_regs+10, Efault); + unsafe_put_user(sw->r11, sc->sc_regs+11, Efault); + unsafe_put_user(sw->r12, sc->sc_regs+12, Efault); + unsafe_put_user(sw->r13, sc->sc_regs+13, Efault); + unsafe_put_user(sw->r14, sc->sc_regs+14, Efault); + unsafe_put_user(sw->r15, sc->sc_regs+15, Efault); + unsafe_put_user(regs->r16, sc->sc_regs+16, Efault); + unsafe_put_user(regs->r17, sc->sc_regs+17, Efault); + unsafe_put_user(regs->r18, sc->sc_regs+18, Efault); + unsafe_put_user(regs->r19, sc->sc_regs+19, Efault); + unsafe_put_user(regs->r20, sc->sc_regs+20, Efault); + unsafe_put_user(regs->r21, sc->sc_regs+21, Efault); + unsafe_put_user(regs->r22, sc->sc_regs+22, Efault); + unsafe_put_user(regs->r23, sc->sc_regs+23, Efault); + unsafe_put_user(regs->r24, sc->sc_regs+24, Efault); + unsafe_put_user(regs->r25, sc->sc_regs+25, Efault); + unsafe_put_user(regs->r26, sc->sc_regs+26, Efault); + unsafe_put_user(regs->r27, sc->sc_regs+27, Efault); + unsafe_put_user(regs->r28, sc->sc_regs+28, Efault); + unsafe_put_user(regs->gp, sc->sc_regs+29, Efault); + unsafe_put_user(sp, sc->sc_regs+30, Efault); + unsafe_put_user(0, sc->sc_regs+31, Efault); - err |= __copy_to_user(sc->sc_fpregs, - current_thread_info()->fp, 31 * 8); - err |= __put_user(0, sc->sc_fpregs+31); - err |= __put_user(current_thread_info()->fp[31], &sc->sc_fpcr); + unsafe_copy_to_user(sc->sc_fpregs, + current_thread_info()->fp, 31 * 8, Efault); + unsafe_put_user(0, sc->sc_fpregs+31, Efault); + unsafe_put_user(current_thread_info()->fp[31], &sc->sc_fpcr, Efault); - err |= __put_user(regs->trap_a0, &sc->sc_traparg_a0); - err |= __put_user(regs->trap_a1, &sc->sc_traparg_a1); - err |= __put_user(regs->trap_a2, &sc->sc_traparg_a2); + unsafe_put_user(regs->trap_a0, &sc->sc_traparg_a0, Efault); + unsafe_put_user(regs->trap_a1, &sc->sc_traparg_a1, Efault); + unsafe_put_user(regs->trap_a2, &sc->sc_traparg_a2, Efault); + } + return 0; - return err; +Efault: + return -EFAULT; } static int setup_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { - unsigned long oldsp, r26, err = 0; + unsigned long oldsp, r26; struct sigframe __user *frame; oldsp = rdusp(); @@ -347,25 +351,22 @@ setup_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) if (!access_ok(frame, sizeof(*frame))) return -EFAULT; - err |= setup_sigcontext(&frame->sc, regs, set->sig[0], oldsp); - if (err) + if (setup_sigcontext(&frame->sc, regs, set->sig[0], oldsp)) return -EFAULT; /* Set up to return from userspace. If provided, use a stub already in userspace. */ r26 = (unsigned long) ksig->ka.ka_restorer; if (!r26) { - err |= __put_user(INSN_MOV_R30_R16, frame->retcode+0); - err |= __put_user(INSN_LDI_R0+__NR_sigreturn, frame->retcode+1); - err |= __put_user(INSN_CALLSYS, frame->retcode+2); + scoped_user_write_access(frame, Efault) { + unsafe_put_user(INSN_MOV_R30_R16, frame->retcode+0, Efault); + unsafe_put_user(INSN_LDI_R0+__NR_sigreturn, frame->retcode+1, Efault); + unsafe_put_user(INSN_CALLSYS, frame->retcode+2, Efault); + } imb(); r26 = (unsigned long) frame->retcode; } - /* Check that everything was written properly. */ - if (err) - return err; - /* "Return" to the handler */ regs->r26 = r26; regs->r27 = regs->pc = (unsigned long) ksig->ka.sa.sa_handler; @@ -379,6 +380,9 @@ setup_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) current->comm, current->pid, frame, regs->pc, regs->r26); #endif return 0; + +Efault: + return -EFAULT; } static int @@ -395,9 +399,11 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) err |= copy_siginfo_to_user(&frame->info, &ksig->info); /* Create the ucontext. */ - err |= __put_user(0, &frame->uc.uc_flags); - err |= __put_user(0, &frame->uc.uc_link); - err |= __put_user(set->sig[0], &frame->uc.uc_osf_sigmask); + scoped_user_write_access(frame, Efault) { + unsafe_put_user(0, &frame->uc.uc_flags, Efault); + unsafe_put_user(0, &frame->uc.uc_link, Efault); + unsafe_put_user(set->sig[0], &frame->uc.uc_osf_sigmask, Efault); + } err |= __save_altstack(&frame->uc.uc_stack, oldsp); err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0], oldsp); @@ -409,17 +415,16 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) already in userspace. */ r26 = (unsigned long) ksig->ka.ka_restorer; if (!r26) { - err |= __put_user(INSN_MOV_R30_R16, frame->retcode+0); - err |= __put_user(INSN_LDI_R0+__NR_rt_sigreturn, - frame->retcode+1); - err |= __put_user(INSN_CALLSYS, frame->retcode+2); + scoped_user_write_access(frame, Efault) { + unsafe_put_user(INSN_MOV_R30_R16, frame->retcode+0, Efault); + unsafe_put_user(INSN_LDI_R0+__NR_rt_sigreturn, + frame->retcode+1, Efault); + unsafe_put_user(INSN_CALLSYS, frame->retcode+2, Efault); + } imb(); r26 = (unsigned long) frame->retcode; } - if (err) - return -EFAULT; - /* "Return" to the handler */ regs->r26 = r26; regs->r27 = regs->pc = (unsigned long) ksig->ka.sa.sa_handler; @@ -434,6 +439,9 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) #endif return 0; + +Efault: + return -EFAULT; } From cdb6b4913a7f793143573129c4a2f679dd0170b1 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 08:45:39 -1000 Subject: [PATCH 32/48] hexagon: use scoped user access for the signal frame setup_sigcontext() and restore_sigcontext() move the saved registers with __put_user() and __get_user(), OR-ing the errors together, relying on the access_ok() done on the whole frame by setup_rt_frame() and sys_rt_sigreturn(). setup_rt_frame() writes the two trampoline magic words the same way. Do those accesses inside scoped_user_{write,read}_access() instead, with unsafe_{put,get}_user() and unsafe_copy_{to,from}_user() for the block of general registers. The access_ok() calls stay, for the __copy_to_user() and __copy_from_user() of the signal mask. A fault part way through now stops at the first failing access instead of carrying on. On sigreturn this also means the general registers are no longer zero-filled past the fault, and the user PC is not updated. Either way the task gets SIGSEGV. hexagon uses the asm-generic accessors and has no user_access_begin(), so the accesses themselves are the same as before. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/hexagon/kernel/signal.c | 90 ++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/arch/hexagon/kernel/signal.c b/arch/hexagon/kernel/signal.c index d301f4621553..76f54b9d707e 100644 --- a/arch/hexagon/kernel/signal.c +++ b/arch/hexagon/kernel/signal.c @@ -34,59 +34,64 @@ static void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) { - unsigned long tmp; - int err = 0; + scoped_user_write_access(sc, Efault) { + unsafe_copy_to_user(&sc->sc_regs.r0, ®s->r00, + 32*sizeof(unsigned long), Efault); - err |= copy_to_user(&sc->sc_regs.r0, ®s->r00, - 32*sizeof(unsigned long)); - - err |= __put_user(regs->sa0, &sc->sc_regs.sa0); - err |= __put_user(regs->lc0, &sc->sc_regs.lc0); - err |= __put_user(regs->sa1, &sc->sc_regs.sa1); - err |= __put_user(regs->lc1, &sc->sc_regs.lc1); - err |= __put_user(regs->m0, &sc->sc_regs.m0); - err |= __put_user(regs->m1, &sc->sc_regs.m1); - err |= __put_user(regs->usr, &sc->sc_regs.usr); - err |= __put_user(regs->preds, &sc->sc_regs.p3_0); - err |= __put_user(regs->gp, &sc->sc_regs.gp); - err |= __put_user(regs->ugp, &sc->sc_regs.ugp); + unsafe_put_user(regs->sa0, &sc->sc_regs.sa0, Efault); + unsafe_put_user(regs->lc0, &sc->sc_regs.lc0, Efault); + unsafe_put_user(regs->sa1, &sc->sc_regs.sa1, Efault); + unsafe_put_user(regs->lc1, &sc->sc_regs.lc1, Efault); + unsafe_put_user(regs->m0, &sc->sc_regs.m0, Efault); + unsafe_put_user(regs->m1, &sc->sc_regs.m1, Efault); + unsafe_put_user(regs->usr, &sc->sc_regs.usr, Efault); + unsafe_put_user(regs->preds, &sc->sc_regs.p3_0, Efault); + unsafe_put_user(regs->gp, &sc->sc_regs.gp, Efault); + unsafe_put_user(regs->ugp, &sc->sc_regs.ugp, Efault); #if CONFIG_HEXAGON_ARCH_VERSION >= 4 - err |= __put_user(regs->cs0, &sc->sc_regs.cs0); - err |= __put_user(regs->cs1, &sc->sc_regs.cs1); + unsafe_put_user(regs->cs0, &sc->sc_regs.cs0, Efault); + unsafe_put_user(regs->cs1, &sc->sc_regs.cs1, Efault); #endif - tmp = pt_elr(regs); err |= __put_user(tmp, &sc->sc_regs.pc); - tmp = pt_cause(regs); err |= __put_user(tmp, &sc->sc_regs.cause); - tmp = pt_badva(regs); err |= __put_user(tmp, &sc->sc_regs.badva); + unsafe_put_user(pt_elr(regs), &sc->sc_regs.pc, Efault); + unsafe_put_user(pt_cause(regs), &sc->sc_regs.cause, Efault); + unsafe_put_user(pt_badva(regs), &sc->sc_regs.badva, Efault); + } + return 0; - return err; +Efault: + return -EFAULT; } static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) { unsigned long tmp; - int err = 0; - err |= copy_from_user(®s->r00, &sc->sc_regs.r0, - 32 * sizeof(unsigned long)); + scoped_user_read_access(sc, Efault) { + unsafe_copy_from_user(®s->r00, &sc->sc_regs.r0, + 32 * sizeof(unsigned long), Efault); - err |= __get_user(regs->sa0, &sc->sc_regs.sa0); - err |= __get_user(regs->lc0, &sc->sc_regs.lc0); - err |= __get_user(regs->sa1, &sc->sc_regs.sa1); - err |= __get_user(regs->lc1, &sc->sc_regs.lc1); - err |= __get_user(regs->m0, &sc->sc_regs.m0); - err |= __get_user(regs->m1, &sc->sc_regs.m1); - err |= __get_user(regs->usr, &sc->sc_regs.usr); - err |= __get_user(regs->preds, &sc->sc_regs.p3_0); - err |= __get_user(regs->gp, &sc->sc_regs.gp); - err |= __get_user(regs->ugp, &sc->sc_regs.ugp); + unsafe_get_user(regs->sa0, &sc->sc_regs.sa0, Efault); + unsafe_get_user(regs->lc0, &sc->sc_regs.lc0, Efault); + unsafe_get_user(regs->sa1, &sc->sc_regs.sa1, Efault); + unsafe_get_user(regs->lc1, &sc->sc_regs.lc1, Efault); + unsafe_get_user(regs->m0, &sc->sc_regs.m0, Efault); + unsafe_get_user(regs->m1, &sc->sc_regs.m1, Efault); + unsafe_get_user(regs->usr, &sc->sc_regs.usr, Efault); + unsafe_get_user(regs->preds, &sc->sc_regs.p3_0, Efault); + unsafe_get_user(regs->gp, &sc->sc_regs.gp, Efault); + unsafe_get_user(regs->ugp, &sc->sc_regs.ugp, Efault); #if CONFIG_HEXAGON_ARCH_VERSION >= 4 - err |= __get_user(regs->cs0, &sc->sc_regs.cs0); - err |= __get_user(regs->cs1, &sc->sc_regs.cs1); + unsafe_get_user(regs->cs0, &sc->sc_regs.cs0, Efault); + unsafe_get_user(regs->cs1, &sc->sc_regs.cs1, Efault); #endif - err |= __get_user(tmp, &sc->sc_regs.pc); pt_set_elr(regs, tmp); + unsafe_get_user(tmp, &sc->sc_regs.pc, Efault); + } + pt_set_elr(regs, tmp); + return 0; - return err; +Efault: + return -EFAULT; } /* @@ -111,8 +116,10 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, * however, the libgcc signal frame unwinding code checks for * the presence of these two numeric magic values. */ - err |= __put_user(0x7800d166, &frame->tramp[0]); - err |= __put_user(0x5400c004, &frame->tramp[1]); + scoped_user_write_access(frame, Efault) { + unsafe_put_user(0x7800d166, &frame->tramp[0], Efault); + unsafe_put_user(0x5400c004, &frame->tramp[1], Efault); + } err |= setup_sigcontext(regs, &frame->uc.uc_mcontext); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); err |= __save_altstack(&frame->uc.uc_stack, user_stack_pointer(regs)); @@ -128,6 +135,9 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, pt_set_elr(regs, (unsigned long)ksig->ka.sa.sa_handler); return 0; + +Efault: + return -EFAULT; } /* From 1d82db25c9b6c9ada9629edd95e2ae8eeebc1cc2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 08:51:13 -1000 Subject: [PATCH 33/48] nios2: check the signal frame address before writing to it setup_rt_frame() never checks that the signal frame it builds is in user space. The frame address comes from the user stack pointer (or the alternate signal stack), and nios2's __put_user() is a plain store with no address check. The kernel runs in supervisor mode above TASK_SIZE, so those stores succeed. The errors are OR-ed together and the frame setup carries on, so even though copy_siginfo_to_user() fails its own check, the ucontext header and every saved register are still written. A task that points its stack pointer at kernel memory and takes a signal gets its own register values written there. Check the whole frame with access_ok() before writing any of it, as the sigreturn side already does. This goes back to the original nios2 signal code in commit b53e906d255d ("nios2: Signal handling support"). Signed-off-by: Linus Torvalds --- arch/nios2/kernel/signal.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/nios2/kernel/signal.c b/arch/nios2/kernel/signal.c index a5b93a30c6eb..73a99c348158 100644 --- a/arch/nios2/kernel/signal.c +++ b/arch/nios2/kernel/signal.c @@ -186,6 +186,8 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, int err = 0; frame = get_sigframe(ksig, regs, sizeof(*frame)); + if (!access_ok(frame, sizeof(*frame))) + goto give_sigsegv; if (ksig->ka.sa.sa_flags & SA_SIGINFO) err |= copy_siginfo_to_user(&frame->info, &ksig->info); From 939e5a481a881e989714754ca65ac35f195e8e34 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 08:51:54 -1000 Subject: [PATCH 34/48] nios2: use scoped user access for the signal frame rt_setup_ucontext() and rt_restore_ucontext() move the saved registers with __put_user() and __get_user(), OR-ing the errors together, relying on the access_ok() done on the whole frame by setup_rt_frame() and do_rt_sigreturn(). setup_rt_frame() writes the ucontext flags and link the same way. Do those accesses inside scoped_user_{write,read}_access() instead, with unsafe_{put,get}_user(). The gregs pointer is now derived inside each scope, from the pointer the scope checked. restore_altstack() is called after the scope. The access_ok() calls stay: do_rt_sigreturn() still needs its own for the __copy_from_user() of the signal mask. A fault part way through now stops at the first failing access instead of carrying on. Either way the task gets SIGSEGV. nios2 has no user_access_begin(), so the accesses themselves are the same as before. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/nios2/kernel/signal.c | 159 +++++++++++++++++++------------------ 1 file changed, 83 insertions(+), 76 deletions(-) diff --git a/arch/nios2/kernel/signal.c b/arch/nios2/kernel/signal.c index 73a99c348158..3ef95d6d8d5c 100644 --- a/arch/nios2/kernel/signal.c +++ b/arch/nios2/kernel/signal.c @@ -39,60 +39,60 @@ static inline int rt_restore_ucontext(struct pt_regs *regs, struct ucontext __user *uc, int *pr2) { int temp; - unsigned long __user *gregs = uc->uc_mcontext.gregs; - int err; /* Always make any pending restarted system calls return -EINTR */ current->restart_block.fn = do_no_restart_syscall; - err = __get_user(temp, &uc->uc_mcontext.version); - if (temp != MCONTEXT_VERSION) - goto badframe; - /* restore passed registers */ - err |= __get_user(regs->r1, &gregs[0]); - err |= __get_user(regs->r2, &gregs[1]); - err |= __get_user(regs->r3, &gregs[2]); - err |= __get_user(regs->r4, &gregs[3]); - err |= __get_user(regs->r5, &gregs[4]); - err |= __get_user(regs->r6, &gregs[5]); - err |= __get_user(regs->r7, &gregs[6]); - err |= __get_user(regs->r8, &gregs[7]); - err |= __get_user(regs->r9, &gregs[8]); - err |= __get_user(regs->r10, &gregs[9]); - err |= __get_user(regs->r11, &gregs[10]); - err |= __get_user(regs->r12, &gregs[11]); - err |= __get_user(regs->r13, &gregs[12]); - err |= __get_user(regs->r14, &gregs[13]); - err |= __get_user(regs->r15, &gregs[14]); - err |= __get_user(sw->r16, &gregs[15]); - err |= __get_user(sw->r17, &gregs[16]); - err |= __get_user(sw->r18, &gregs[17]); - err |= __get_user(sw->r19, &gregs[18]); - err |= __get_user(sw->r20, &gregs[19]); - err |= __get_user(sw->r21, &gregs[20]); - err |= __get_user(sw->r22, &gregs[21]); - err |= __get_user(sw->r23, &gregs[22]); - /* gregs[23] is handled below */ - err |= __get_user(sw->fp, &gregs[24]); /* Verify, should this be - settable */ - err |= __get_user(sw->gp, &gregs[25]); /* Verify, should this be - settable */ + scoped_user_read_access(uc, badframe) { + unsigned long __user *gregs = uc->uc_mcontext.gregs; - err |= __get_user(temp, &gregs[26]); /* Not really necessary no user - settable bits */ - err |= __get_user(regs->ea, &gregs[27]); + unsafe_get_user(temp, &uc->uc_mcontext.version, badframe); + if (temp != MCONTEXT_VERSION) + goto badframe; + /* restore passed registers */ + unsafe_get_user(regs->r1, &gregs[0], badframe); + unsafe_get_user(regs->r2, &gregs[1], badframe); + unsafe_get_user(regs->r3, &gregs[2], badframe); + unsafe_get_user(regs->r4, &gregs[3], badframe); + unsafe_get_user(regs->r5, &gregs[4], badframe); + unsafe_get_user(regs->r6, &gregs[5], badframe); + unsafe_get_user(regs->r7, &gregs[6], badframe); + unsafe_get_user(regs->r8, &gregs[7], badframe); + unsafe_get_user(regs->r9, &gregs[8], badframe); + unsafe_get_user(regs->r10, &gregs[9], badframe); + unsafe_get_user(regs->r11, &gregs[10], badframe); + unsafe_get_user(regs->r12, &gregs[11], badframe); + unsafe_get_user(regs->r13, &gregs[12], badframe); + unsafe_get_user(regs->r14, &gregs[13], badframe); + unsafe_get_user(regs->r15, &gregs[14], badframe); + unsafe_get_user(sw->r16, &gregs[15], badframe); + unsafe_get_user(sw->r17, &gregs[16], badframe); + unsafe_get_user(sw->r18, &gregs[17], badframe); + unsafe_get_user(sw->r19, &gregs[18], badframe); + unsafe_get_user(sw->r20, &gregs[19], badframe); + unsafe_get_user(sw->r21, &gregs[20], badframe); + unsafe_get_user(sw->r22, &gregs[21], badframe); + unsafe_get_user(sw->r23, &gregs[22], badframe); + /* gregs[23] is handled below */ + /* Verify, should these be settable */ + unsafe_get_user(sw->fp, &gregs[24], badframe); + unsafe_get_user(sw->gp, &gregs[25], badframe); - err |= __get_user(regs->ra, &gregs[23]); - err |= __get_user(regs->sp, &gregs[28]); + /* Not really necessary no user settable bits */ + unsafe_get_user(temp, &gregs[26], badframe); + unsafe_get_user(regs->ea, &gregs[27], badframe); + + unsafe_get_user(regs->ra, &gregs[23], badframe); + unsafe_get_user(regs->sp, &gregs[28], badframe); + } regs->orig_r2 = -1; /* disable syscall checks */ - err |= restore_altstack(&uc->uc_stack); - if (err) + if (restore_altstack(&uc->uc_stack)) goto badframe; *pr2 = regs->r2; - return err; + return 0; badframe: return 1; @@ -128,39 +128,44 @@ asmlinkage int do_rt_sigreturn(struct switch_stack *sw) static inline int rt_setup_ucontext(struct ucontext __user *uc, struct pt_regs *regs) { struct switch_stack *sw = (struct switch_stack *)regs - 1; - unsigned long __user *gregs = uc->uc_mcontext.gregs; - int err = 0; - err |= __put_user(MCONTEXT_VERSION, &uc->uc_mcontext.version); - err |= __put_user(regs->r1, &gregs[0]); - err |= __put_user(regs->r2, &gregs[1]); - err |= __put_user(regs->r3, &gregs[2]); - err |= __put_user(regs->r4, &gregs[3]); - err |= __put_user(regs->r5, &gregs[4]); - err |= __put_user(regs->r6, &gregs[5]); - err |= __put_user(regs->r7, &gregs[6]); - err |= __put_user(regs->r8, &gregs[7]); - err |= __put_user(regs->r9, &gregs[8]); - err |= __put_user(regs->r10, &gregs[9]); - err |= __put_user(regs->r11, &gregs[10]); - err |= __put_user(regs->r12, &gregs[11]); - err |= __put_user(regs->r13, &gregs[12]); - err |= __put_user(regs->r14, &gregs[13]); - err |= __put_user(regs->r15, &gregs[14]); - err |= __put_user(sw->r16, &gregs[15]); - err |= __put_user(sw->r17, &gregs[16]); - err |= __put_user(sw->r18, &gregs[17]); - err |= __put_user(sw->r19, &gregs[18]); - err |= __put_user(sw->r20, &gregs[19]); - err |= __put_user(sw->r21, &gregs[20]); - err |= __put_user(sw->r22, &gregs[21]); - err |= __put_user(sw->r23, &gregs[22]); - err |= __put_user(regs->ra, &gregs[23]); - err |= __put_user(sw->fp, &gregs[24]); - err |= __put_user(sw->gp, &gregs[25]); - err |= __put_user(regs->ea, &gregs[27]); - err |= __put_user(regs->sp, &gregs[28]); - return err; + scoped_user_write_access(uc, Efault) { + unsigned long __user *gregs = uc->uc_mcontext.gregs; + + unsafe_put_user(MCONTEXT_VERSION, &uc->uc_mcontext.version, Efault); + unsafe_put_user(regs->r1, &gregs[0], Efault); + unsafe_put_user(regs->r2, &gregs[1], Efault); + unsafe_put_user(regs->r3, &gregs[2], Efault); + unsafe_put_user(regs->r4, &gregs[3], Efault); + unsafe_put_user(regs->r5, &gregs[4], Efault); + unsafe_put_user(regs->r6, &gregs[5], Efault); + unsafe_put_user(regs->r7, &gregs[6], Efault); + unsafe_put_user(regs->r8, &gregs[7], Efault); + unsafe_put_user(regs->r9, &gregs[8], Efault); + unsafe_put_user(regs->r10, &gregs[9], Efault); + unsafe_put_user(regs->r11, &gregs[10], Efault); + unsafe_put_user(regs->r12, &gregs[11], Efault); + unsafe_put_user(regs->r13, &gregs[12], Efault); + unsafe_put_user(regs->r14, &gregs[13], Efault); + unsafe_put_user(regs->r15, &gregs[14], Efault); + unsafe_put_user(sw->r16, &gregs[15], Efault); + unsafe_put_user(sw->r17, &gregs[16], Efault); + unsafe_put_user(sw->r18, &gregs[17], Efault); + unsafe_put_user(sw->r19, &gregs[18], Efault); + unsafe_put_user(sw->r20, &gregs[19], Efault); + unsafe_put_user(sw->r21, &gregs[20], Efault); + unsafe_put_user(sw->r22, &gregs[21], Efault); + unsafe_put_user(sw->r23, &gregs[22], Efault); + unsafe_put_user(regs->ra, &gregs[23], Efault); + unsafe_put_user(sw->fp, &gregs[24], Efault); + unsafe_put_user(sw->gp, &gregs[25], Efault); + unsafe_put_user(regs->ea, &gregs[27], Efault); + unsafe_put_user(regs->sp, &gregs[28], Efault); + } + return 0; + +Efault: + return -EFAULT; } static inline void __user *get_sigframe(struct ksignal *ksig, @@ -193,8 +198,10 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, err |= copy_siginfo_to_user(&frame->info, &ksig->info); /* Create the ucontext. */ - err |= __put_user(0, &frame->uc.uc_flags); - err |= __put_user(0, &frame->uc.uc_link); + scoped_user_write_access(frame, give_sigsegv) { + unsafe_put_user(0, &frame->uc.uc_flags, give_sigsegv); + unsafe_put_user(0, &frame->uc.uc_link, give_sigsegv); + } err |= __save_altstack(&frame->uc.uc_stack, regs->sp); err |= rt_setup_ucontext(&frame->uc, regs); err |= copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); From 8eb93b8a67181827611d2a242de5983a04f2bc8c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 09:26:44 -1000 Subject: [PATCH 35/48] alpha: use get_kernel_nofault() in dik_show_code() dik_show_code() dumps the instructions around the PC when the kernel dies. Both callers, die_if_kernel() and the kernel unaligned-access handler, only get there for a kernel-mode PC: die_if_kernel() returns early for user mode, and user-mode unaligned traps go to do_entUnaUser() instead. It reads that kernel text with __get_user() through a cast to a user pointer. That works only because alpha's __get_user() does no address check, and it predates get_kernel_nofault(), which is the interface for exactly this: a kernel address that may not be mapped, read with page faults disabled. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/alpha/kernel/traps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c index cfaef96c4ce8..9247aa28219a 100644 --- a/arch/alpha/kernel/traps.c +++ b/arch/alpha/kernel/traps.c @@ -87,7 +87,7 @@ dik_show_code(unsigned int *pc) printk("Code:"); for (i = -6; i < 2; i++) { unsigned int insn; - if (__get_user(insn, (unsigned int __user *)pc + i)) + if (get_kernel_nofault(insn, pc + i)) break; printk("%c%08x%c", i ? ' ' : '<', insn, i ? ' ' : '>'); } From 5a36c24b0ec8fe15ddffa3148ae93a4691ea7c8f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 09:29:11 -1000 Subject: [PATCH 36/48] openrisc: use get_kernel_nofault() in show_registers() show_registers() dumps the stack and the code around the PC, but only for a kernel-mode fault, and only after checking that each address is at or above PAGE_OFFSET. Both are kernel addresses that may not be mapped. It reads them with __get_user(), which predates get_kernel_nofault(), the interface for exactly this: a kernel address that may not be mapped, read with page faults disabled. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/openrisc/kernel/traps.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/openrisc/kernel/traps.c b/arch/openrisc/kernel/traps.c index c195be9cc9fc..2ad5e141981d 100644 --- a/arch/openrisc/kernel/traps.c +++ b/arch/openrisc/kernel/traps.c @@ -124,7 +124,7 @@ void show_registers(struct pt_regs *regs) for (i = -8; i < 24; i += 1) { unsigned long word; - if (__get_user(word, &((unsigned long *)esp)[i])) { + if (get_kernel_nofault(word, &((unsigned long *)esp)[i])) { bad_stack: pr_info(" Bad Stack value."); break; @@ -140,7 +140,7 @@ void show_registers(struct pt_regs *regs) for (i = -6; i < 6; i += 1) { unsigned long word; - if (__get_user(word, &((unsigned long *)regs->pc)[i])) { + if (get_kernel_nofault(word, &((unsigned long *)regs->pc)[i])) { bad: pr_info(" Bad PC value."); break; From 7318a05d2f4b202d8e1d6f5a77a617f79e1526fb Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 09:41:32 -1000 Subject: [PATCH 37/48] alpha: split the prefetch check in do_page_fault() by mode Before EV6, a load into $31 or $f31 could fault, so do_page_fault() reads the faulting instruction and skips it if it is such a prefetch. The PC can be a user or a kernel address, since the check runs for faults from either mode, and it reads it with __get_user() either way. That only works because alpha's __get_user() does no address check. It also ignores the error, relying on the fault fixup zeroing the instruction, which does not decode as a prefetch. Move the check into is_prefetch(), and read the instruction with get_user() for a user-mode PC and get_kernel_nofault() for a kernel one. A failed read now explicitly means "not a prefetch". This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/alpha/mm/fault.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c index a9816bbc9f34..3435a54888b0 100644 --- a/arch/alpha/mm/fault.c +++ b/arch/alpha/mm/fault.c @@ -81,6 +81,28 @@ __load_new_mm_context(struct mm_struct *next_mm) (((unsigned long *)regs)[(r) <= 8 ? (r) : (r) <= 15 ? (r)-17 : \ (r) <= 18 ? (r)+11 : (r)-10]) +/* + * As of EV6, a load into $31/$f31 is a prefetch, and never faults + * (or is suppressed by the PALcode). Support that for older CPUs + * by ignoring such an instruction. + */ +static bool is_prefetch(struct pt_regs *regs) +{ + unsigned int insn; + int err; + + if (user_mode(regs)) + err = get_user(insn, (unsigned int __user *)regs->pc); + else + err = get_kernel_nofault(insn, (unsigned int *)regs->pc); + if (err) + return false; + + /* ldq ldl ldt lds ldg ldf ldwu ldbu */ + return (insn >> 21 & 0x1f) == 0x1f && + (1ul << (insn >> 26) & 0x30f00001400ul); +} + asmlinkage void do_page_fault(unsigned long address, unsigned long mmcsr, long cause, struct pt_regs *regs) @@ -92,18 +114,9 @@ do_page_fault(unsigned long address, unsigned long mmcsr, vm_fault_t fault; unsigned int flags = FAULT_FLAG_DEFAULT; - /* As of EV6, a load into $31/$f31 is a prefetch, and never faults - (or is suppressed by the PALcode). Support that for older CPUs - by ignoring such an instruction. */ - if (cause == 0) { - unsigned int insn; - __get_user(insn, (unsigned int __user *)regs->pc); - if ((insn >> 21 & 0x1f) == 0x1f && - /* ldq ldl ldt lds ldg ldf ldwu ldbu */ - (1ul << (insn >> 26) & 0x30f00001400ul)) { - regs->pc += 4; - return; - } + if (cause == 0 && is_prefetch(regs)) { + regs->pc += 4; + return; } /* If we're in an interrupt context, or have no user context, From 0a4e3e979b5ecb2460ab8eb8f4f8aba3590a4043 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 09:49:35 -1000 Subject: [PATCH 38/48] ARM: signal: check the FDPIC function descriptor address When a task has FDPIC_FUNCPTRS in its personality, setup_return() treats the signal handler as a pointer to a function descriptor and reads the entry point and GOT pointer from it with __get_user(). Nothing checks that pointer: it is whatever the task passed to sigaction(), and any task can set FDPIC_FUNCPTRS with personality(2). BINFMT_ELF_FDPIC is not limited to !MMU on ARM. On most configurations this is harmless only by accident. With CPU_SPECTRE (ARMv7) __get_user() is get_user(). With CPU_USE_DOMAINS (ARMv5 and older) it uses the unprivileged ldrt, but that only stops at kernel memory when software PAN has put the kernel domain in client mode; with ARM_PAN=n the kernel domain stays in manager mode, where permissions are not checked. So on an ARMv6-only MMU kernel, or an ARMv5 or older one without ARM_PAN, it reads two words of kernel memory that end up in the task's PC and r9. Use get_user(), which checks the address. This goes back to commit 5c16595353e0 ("ARM: signal handling support for FDPIC_FUNCPTRS functions"). Signed-off-by: Linus Torvalds --- arch/arm/kernel/signal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 7be9188d83d9..d5d15b9ec4ab 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -332,8 +332,8 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig, if (fdpic) { unsigned long __user *fdpic_func_desc = (unsigned long __user *)handler; - if (__get_user(handler, &fdpic_func_desc[0]) || - __get_user(handler_fdpic_GOT, &fdpic_func_desc[1])) + if (get_user(handler, &fdpic_func_desc[0]) || + get_user(handler_fdpic_GOT, &fdpic_func_desc[1])) return 1; } From 600740a41fb51be829607f654217277265ea0e4a Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 09:50:54 -1000 Subject: [PATCH 39/48] ARM: signal: stop using __put_user() for the signal frame The signal frame is checked with access_ok() once, in get_sigframe(), and a few of its fields are then written with __put_user(): the ucontext flags and link, the end marker after the coprocessor state, and the sigreturn trampoline words. Use scoped_user_write_access() with unsafe_put_user() where there are several writes in a row, and put_user() for the two single ones. ARM has no user_access_begin(), so the scopes are the same access_ok() and the same accesses as before. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/arm/kernel/signal.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index d5d15b9ec4ab..763e1930a1ea 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -292,7 +292,7 @@ setup_sigframe(struct sigframe __user *sf, struct pt_regs *regs, sigset_t *set) if (err == 0) err |= preserve_vfp_context(&aux->vfp); #endif - err |= __put_user(0, &aux->end_magic); + err |= put_user(0, &aux->end_magic); return err; } @@ -387,11 +387,12 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig, idx = 6 + thumb * 3; if (ksig->ka.sa.sa_flags & SA_SIGINFO) idx += 5; - if (__put_user(sigreturn_codes[idx], rc ) || - __put_user(sigreturn_codes[idx+1], rc+1) || - __put_user(sigreturn_codes[idx+2], rc+2) || - __put_user(retcode, rc+3)) - return 1; + scoped_user_write_access_size(rc, 4 * sizeof(*rc), efault) { + unsafe_put_user(sigreturn_codes[idx], rc, efault); + unsafe_put_user(sigreturn_codes[idx+1], rc+1, efault); + unsafe_put_user(sigreturn_codes[idx+2], rc+2, efault); + unsafe_put_user(retcode, rc+3, efault); + } goto rc_finish; } } else { @@ -403,9 +404,10 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig, * Put the sigreturn code on the stack no matter which return * mechanism we use in order to remain ABI compliant */ - if (__put_user(sigreturn_codes[idx], rc) || - __put_user(sigreturn_codes[idx+1], rc+1)) - return 1; + scoped_user_write_access_size(rc, 2 * sizeof(*rc), efault) { + unsafe_put_user(sigreturn_codes[idx], rc, efault); + unsafe_put_user(sigreturn_codes[idx+1], rc+1, efault); + } rc_finish: #ifdef CONFIG_MMU @@ -442,6 +444,9 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig, regs->ARM_cpsr = cpsr; return 0; + +efault: + return 1; } static int @@ -456,7 +461,7 @@ setup_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) /* * Set uc.uc_flags to a value which sc.trap_no would never have. */ - err = __put_user(0x5ac3c35a, &frame->uc.uc_flags); + err = put_user(0x5ac3c35a, &frame->uc.uc_flags); err |= setup_sigframe(frame, regs, set); if (err == 0) @@ -476,8 +481,10 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) err |= copy_siginfo_to_user(&frame->info, &ksig->info); - err |= __put_user(0, &frame->sig.uc.uc_flags); - err |= __put_user(NULL, &frame->sig.uc.uc_link); + scoped_user_write_access(frame, efault) { + unsafe_put_user(0, &frame->sig.uc.uc_flags, efault); + unsafe_put_user(NULL, &frame->sig.uc.uc_link, efault); + } err |= __save_altstack(&frame->sig.uc.uc_stack, regs->ARM_sp); err |= setup_sigframe(&frame->sig, regs, set); @@ -495,6 +502,9 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) } return err; + +efault: + return -EFAULT; } /* From 48e5959144e40506b38b383ce0e9feed95cdf68a Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 09:51:16 -1000 Subject: [PATCH 40/48] ARM: OABI: use scoped user access in epoll_put_uevent() The generic epoll_put_uevent() writes the two fields of each event inside scoped_user_write_access_size(). The ARM override, which also handles the packed OABI layout, still does it with __put_user(), relying on the access_ok() that ep_check_params() does on the whole array. Do it the same way as the generic version, for both layouts. ARM has no user_access_begin(), so the accesses are the same as before. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/arm/kernel/sys_oabi-compat.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/arch/arm/kernel/sys_oabi-compat.c b/arch/arm/kernel/sys_oabi-compat.c index 3d7f7fcf2d87..2917b927162a 100644 --- a/arch/arm/kernel/sys_oabi-compat.c +++ b/arch/arm/kernel/sys_oabi-compat.c @@ -310,18 +310,21 @@ epoll_put_uevent(__poll_t revents, __u64 data, if (in_oabi_syscall()) { struct oabi_epoll_event __user *oevent = (void __user *)uevent; - if (__put_user(revents, &oevent->events) || - __put_user(data, &oevent->data)) - return NULL; - + scoped_user_write_access(oevent, efault) { + unsafe_put_user(revents, &oevent->events, efault); + unsafe_put_user(data, &oevent->data, efault); + } return (void __user *)(oevent+1); } - if (__put_user(revents, &uevent->events) || - __put_user(data, &uevent->data)) - return NULL; - + scoped_user_write_access(uevent, efault) { + unsafe_put_user(revents, &uevent->events, efault); + unsafe_put_user(data, &uevent->data, efault); + } return uevent+1; + +efault: + return NULL; } struct oabi_sembuf { From bb4e937ff6857ca9277d35683682ea4c1f75ff2a Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 09:55:28 -1000 Subject: [PATCH 41/48] s390: use get_user()/put_user() instead of the unchecked variants On s390 user space is a separate address space, so access_ok() has nothing to check, and get_user() and put_user() are __get_user() and __put_user() plus might_fault(). The remaining callers can use the normal accessors directly: - signal frame setup writes the back chain, the signal number and the ucontext header in process context; - the user stack walker for stack traces and perf callchains already runs under pagefault_disable(), where might_fault() returns early; - the general register set writer for ptrace runs in the tracer's process context with no locks held. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/s390/kernel/ptrace.c | 2 +- arch/s390/kernel/signal.c | 10 +++++----- arch/s390/kernel/stacktrace.c | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 125ca4c4e30c..06c6901d7c41 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -540,7 +540,7 @@ static int s390_regs_set(struct task_struct *target, const unsigned long __user *u = ubuf; while (count > 0 && !rc) { unsigned long word; - rc = __get_user(word, u++); + rc = get_user(word, u++); if (rc) break; rc = __poke_user(target, pos, word); diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index 4874de5edea0..81709cd2e9e5 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c @@ -297,7 +297,7 @@ static int setup_frame(int sig, struct k_sigaction *ka, return -EFAULT; /* Set up backchain. */ - if (__put_user(regs->gprs[15], (addr_t __user *) frame)) + if (put_user(regs->gprs[15], (addr_t __user *) frame)) return -EFAULT; /* Create struct sigcontext on the signal stack */ @@ -314,7 +314,7 @@ static int setup_frame(int sig, struct k_sigaction *ka, return -EFAULT; /* Place signal number on stack to allow backtrace from handler. */ - if (__put_user(regs->gprs[2], (int __user *) &frame->signo)) + if (put_user(regs->gprs[2], (int __user *) &frame->signo)) return -EFAULT; /* Create _sigregs_ext on the signal stack */ @@ -370,7 +370,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, return -EFAULT; /* Set up backchain. */ - if (__put_user(regs->gprs[15], (addr_t __user *) frame)) + if (put_user(regs->gprs[15], (addr_t __user *) frame)) return -EFAULT; /* Set up to return from userspace. If provided, use a stub @@ -388,8 +388,8 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, store_sigregs(); /* Create ucontext on the signal stack. */ - if (__put_user(uc_flags, &frame->uc.uc_flags) || - __put_user(NULL, &frame->uc.uc_link) || + if (put_user(uc_flags, &frame->uc.uc_flags) || + put_user(NULL, &frame->uc.uc_link) || __save_altstack(&frame->uc.uc_stack, regs->gprs[15]) || save_sigregs(regs, &frame->uc.uc_mcontext) || __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)) || diff --git a/arch/s390/kernel/stacktrace.c b/arch/s390/kernel/stacktrace.c index 18520d333058..191f8a93371e 100644 --- a/arch/s390/kernel/stacktrace.c +++ b/arch/s390/kernel/stacktrace.c @@ -113,7 +113,7 @@ void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *coo sf = (void __user *)user_stack_pointer(regs); pagefault_disable(); while (1) { - if (__get_user(sp, &sf->back_chain)) + if (get_user(sp, &sf->back_chain)) break; /* * VDSO entry code has a non-standard stack frame layout. @@ -121,15 +121,15 @@ void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *coo */ if (!sp && ip_within_vdso(ip)) { sf_vdso = (void __user *)sf; - if (__get_user(ip, &sf_vdso->return_address)) + if (get_user(ip, &sf_vdso->return_address)) break; sp = (unsigned long)sf + STACK_FRAME_VDSO_OVERHEAD; sf = (void __user *)sp; - if (__get_user(sp, &sf->back_chain)) + if (get_user(sp, &sf->back_chain)) break; } else { sf = (void __user *)sp; - if (__get_user(ip, &sf->gprs[8])) + if (get_user(ip, &sf->gprs[8])) break; } /* Validate SP and RA (ABI requires SP to be 8 byte aligned). */ From 05b163dab2bca452ef61e6ee6b12183c67f01aca Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 10:02:27 -1000 Subject: [PATCH 42/48] powerpc/powernv: use get_user()/put_user() in the LPC debugfs files lpc_debug_read() and lpc_debug_write() check the user buffer with access_ok() once and then move it one access at a time with __put_user() or __get_user(), with an OPAL call in between each. Each of those accesses opens and closes user access on its own, so the up-front check saves nothing. Use put_user() and get_user(). The access_ok() stays, so a bad buffer still fails before any LPC cycle is done. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/powerpc/platforms/powernv/opal-lpc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/platforms/powernv/opal-lpc.c b/arch/powerpc/platforms/powernv/opal-lpc.c index 2bb326b58ff8..2e6cc2c89c02 100644 --- a/arch/powerpc/platforms/powernv/opal-lpc.c +++ b/arch/powerpc/platforms/powernv/opal-lpc.c @@ -247,19 +247,19 @@ static ssize_t lpc_debug_read(struct file *filp, char __user *ubuf, */ switch(len) { case 4: - rc = __put_user((u32)data, (u32 __user *)ubuf); + rc = put_user((u32)data, (u32 __user *)ubuf); break; case 2: #ifdef __LITTLE_ENDIAN__ data >>= 16; #endif - rc = __put_user((u16)data, (u16 __user *)ubuf); + rc = put_user((u16)data, (u16 __user *)ubuf); break; default: #ifdef __LITTLE_ENDIAN__ data >>= 24; #endif - rc = __put_user((u8)data, (u8 __user *)ubuf); + rc = put_user((u8)data, (u8 __user *)ubuf); break; } if (rc) @@ -317,15 +317,15 @@ static ssize_t lpc_debug_write(struct file *filp, const char __user *ubuf, */ switch(len) { case 4: - rc = __get_user(data, (u32 __user *)ubuf); + rc = get_user(data, (u32 __user *)ubuf); data = cpu_to_be32(data); break; case 2: - rc = __get_user(data, (u16 __user *)ubuf); + rc = get_user(data, (u16 __user *)ubuf); data = cpu_to_be16(data); break; default: - rc = __get_user(data, (u8 __user *)ubuf); + rc = get_user(data, (u8 __user *)ubuf); break; } if (rc) From a79bb50808a58482eaf39d8e15fbc388cbea3a74 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 10:02:53 -1000 Subject: [PATCH 43/48] KVM: PPC: Book3S HV: use get_user()/put_user() for the HPT fd kvm_htab_read() and kvm_htab_write() check the user buffer with access_ok() once and then move each HPT entry with two __put_user() or __get_user() calls, with real work between entries. Each of those accesses opens and closes user access on its own, so the up-front check saves nothing. Use put_user() and get_user(). On powerpc the only difference is the address check: __get_user() and __put_user() already do might_fault(). kvm_htab_write() holds mmu_setup_lock across the loop, but it already reads each header with __copy_from_user() under that lock, so nothing new happens under it. The access_ok() calls stay. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/powerpc/kvm/book3s_64_mmu_hv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c index 2ccb3d138f46..00b8a3f855f4 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_hv.c +++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c @@ -1749,8 +1749,8 @@ static ssize_t kvm_htab_read(struct file *file, char __user *buf, record_hpte(flags, hptp, hpte, revp, 1, first_pass)) { /* valid entry, write it out */ ++hdr.n_valid; - if (__put_user(hpte[0], lbuf) || - __put_user(hpte[1], lbuf + 1)) + if (put_user(hpte[0], lbuf) || + put_user(hpte[1], lbuf + 1)) return -EFAULT; nb += HPTE_SIZE; lbuf += 2; @@ -1853,8 +1853,8 @@ static ssize_t kvm_htab_write(struct file *file, const char __user *buf, __be64 hpte_r; err = -EFAULT; - if (__get_user(hpte_v, lbuf) || - __get_user(hpte_r, lbuf + 1)) + if (get_user(hpte_v, lbuf) || + get_user(hpte_r, lbuf + 1)) goto out; v = be64_to_cpu(hpte_v); r = be64_to_cpu(hpte_r); From cc3ff64abc0dae390d72770e961be1776d580d93 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 10:03:26 -1000 Subject: [PATCH 44/48] powerpc/signal32: use get_user() for the single context reads A few places in the 32-bit and compat signal code read a single field with __get_user(): the uc_regs and MSR words in swapcontext and the transactional-memory sigreturn and setcontext paths, and uc_link in rt_sigreturn. They are not part of a run of accesses, and either rely on an earlier access_ok() or on compat pointers being below 4GB. Use get_user(). On powerpc __get_user() already does might_fault() and opens user access for the read itself, so the only difference is the address check, which these pointers pass anyway. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/powerpc/kernel/signal_32.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index 7a718ed32b27..1c9fa00dc80d 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -968,7 +968,7 @@ static int do_setcontext_tm(struct ucontext __user *ucp, user_read_access_end(); - if (__get_user(tm_cmcp, &tm_ucp->uc_regs)) + if (get_user(tm_cmcp, &tm_ucp->uc_regs)) return -EFAULT; mcp = (struct mcontext __user *)(u64)cmcp; tm_mcp = (struct mcontext __user *)(u64)tm_cmcp; @@ -1009,10 +1009,10 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx, * access_ok since we are dealing with compat * pointers. */ - if (__get_user(cmcp, &new_ctx->uc_regs)) + if (get_user(cmcp, &new_ctx->uc_regs)) return -EFAULT; mcp = (struct mcontext __user *)(u64)cmcp; - if (__get_user(new_msr, &mcp->mc_gregs[PT_MSR])) + if (get_user(new_msr, &mcp->mc_gregs[PT_MSR])) return -EFAULT; } /* @@ -1124,19 +1124,19 @@ SYSCALL_DEFINE0(rt_sigreturn) if (MSR_TM_SUSPENDED(mfmsr())) tm_reclaim_current(0); - if (__get_user(tmp, &rt_sf->uc.uc_link)) + if (get_user(tmp, &rt_sf->uc.uc_link)) goto bad; uc_transact = (struct ucontext __user *)(uintptr_t)tmp; if (uc_transact) { u32 cmcp; struct mcontext __user *mcp; - if (__get_user(cmcp, &uc_transact->uc_regs)) + if (get_user(cmcp, &uc_transact->uc_regs)) return -EFAULT; mcp = (struct mcontext __user *)(u64)cmcp; /* The top 32 bits of the MSR are stashed in the transactional * ucontext. */ - if (__get_user(msr_hi, &mcp->mc_gregs[PT_MSR])) + if (get_user(msr_hi, &mcp->mc_gregs[PT_MSR])) goto bad; if (MSR_TM_ACTIVE(msr_hi<<32)) { @@ -1330,7 +1330,7 @@ SYSCALL_DEFINE0(sigreturn) mcp = (struct mcontext __user *)&sf->mctx; #ifdef CONFIG_PPC_TRANSACTIONAL_MEM tm_mcp = (struct mcontext __user *)&sf->mctx_transact; - if (__get_user(msr_hi, &tm_mcp->mc_gregs[PT_MSR])) + if (get_user(msr_hi, &tm_mcp->mc_gregs[PT_MSR])) goto badframe; #endif if (MSR_TM_ACTIVE(msr_hi<<32)) { From 2f0bb47c0007d9293154e192a0598f8978a97b96 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 10:04:08 -1000 Subject: [PATCH 45/48] powerpc/signal64: use get_user() for the single context reads swapcontext and rt_sigreturn read the signal mask with __get_user_sigset(), and rt_sigreturn also reads the MSR and uc_link words of the transactional-memory path, each with one __get_user() after an access_ok() on the whole context. Make the helper get_user_sigset(), built on get_user(), and use get_user() for the other two. On powerpc __get_user() already does might_fault() and opens user access for the read itself, so the only difference is the address check, which these pointers pass anyway. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/powerpc/kernel/signal.h | 4 ++-- arch/powerpc/kernel/signal_64.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/kernel/signal.h b/arch/powerpc/kernel/signal.h index 58ecea1cdc27..9e09b55177a6 100644 --- a/arch/powerpc/kernel/signal.h +++ b/arch/powerpc/kernel/signal.h @@ -16,11 +16,11 @@ extern int handle_signal32(struct ksignal *ksig, sigset_t *oldset, extern int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset, struct task_struct *tsk); -static inline int __get_user_sigset(sigset_t *dst, const sigset_t __user *src) +static inline int get_user_sigset(sigset_t *dst, const sigset_t __user *src) { BUILD_BUG_ON(sizeof(sigset_t) != sizeof(u64)); - return __get_user(dst->sig[0], (u64 __user *)&src->sig[0]); + return get_user(dst->sig[0], (u64 __user *)&src->sig[0]); } #define unsafe_get_user_sigset(dst, src, label) do { \ sigset_t *__dst = dst; \ diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 86bb5bb4c143..0610573cfcd5 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -711,7 +711,7 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx, * We kill the task with a SIGSEGV in this situation. */ - if (__get_user_sigset(&set, &new_ctx->uc_sigmask)) { + if (get_user_sigset(&set, &new_ctx->uc_sigmask)) { force_exit_sig(SIGSEGV); return -EFAULT; } @@ -754,7 +754,7 @@ SYSCALL_DEFINE0(rt_sigreturn) if (!access_ok(uc, sizeof(*uc))) goto badframe; - if (__get_user_sigset(&set, &uc->uc_sigmask)) + if (get_user_sigset(&set, &uc->uc_sigmask)) goto badframe; set_current_blocked(&set); @@ -797,7 +797,7 @@ SYSCALL_DEFINE0(rt_sigreturn) */ regs_set_return_msr(regs, regs->msr & ~MSR_TS_MASK); - if (__get_user(msr, &uc->uc_mcontext.gp_regs[PT_MSR])) + if (get_user(msr, &uc->uc_mcontext.gp_regs[PT_MSR])) goto badframe; } @@ -809,7 +809,7 @@ SYSCALL_DEFINE0(rt_sigreturn) if (!cpu_has_feature(CPU_FTR_TM)) goto badframe; - if (__get_user(uc_transact, &uc->uc_link)) + if (get_user(uc_transact, &uc->uc_link)) goto badframe; if (restore_tm_sigcontexts(current, &uc->uc_mcontext, &uc_transact->uc_mcontext)) From 52907f0e4765cba370ef07d536cf4e4ecea8c0ba Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 10:05:11 -1000 Subject: [PATCH 46/48] powerpc/signal64: use scoped user access in setup_trampoline() setup_trampoline() writes the seven words of the on-stack sigreturn trampoline with __put_user(), relying on the frame having been checked when the frame setup opened its user write access. It is called after that access has been closed again, so each of the seven writes opens and closes user access on its own. Write them inside one scoped_user_write_access_size() covering the whole trampoline instead. Everything in it is a store, so a write-only window is enough, and the instruction cache flush stays outside it. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/powerpc/kernel/signal_64.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 0610573cfcd5..b2003a10b369 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -624,24 +624,27 @@ static long restore_tm_sigcontexts(struct task_struct *tsk, struct sigcontext __ static long setup_trampoline(unsigned int syscall, unsigned int __user *tramp) { int i; - long err = 0; - /* Call the handler and pop the dummy stackframe*/ - err |= __put_user(PPC_RAW_BCTRL(), &tramp[0]); - err |= __put_user(PPC_RAW_ADDI(_R1, _R1, __SIGNAL_FRAMESIZE), &tramp[1]); + scoped_user_write_access_size(tramp, TRAMP_SIZE * sizeof(*tramp), efault) { + /* Call the handler and pop the dummy stackframe*/ + unsafe_put_user(PPC_RAW_BCTRL(), &tramp[0], efault); + unsafe_put_user(PPC_RAW_ADDI(_R1, _R1, __SIGNAL_FRAMESIZE), &tramp[1], efault); - err |= __put_user(PPC_RAW_LI(_R0, syscall), &tramp[2]); - err |= __put_user(PPC_RAW_SC(), &tramp[3]); + unsafe_put_user(PPC_RAW_LI(_R0, syscall), &tramp[2], efault); + unsafe_put_user(PPC_RAW_SC(), &tramp[3], efault); - /* Minimal traceback info */ - for (i=TRAMP_TRACEBACK; i < TRAMP_SIZE ;i++) - err |= __put_user(0, &tramp[i]); + /* Minimal traceback info */ + for (i=TRAMP_TRACEBACK; i < TRAMP_SIZE ;i++) + unsafe_put_user(0, &tramp[i], efault); + } - if (!err) - flush_icache_range((unsigned long) &tramp[0], + flush_icache_range((unsigned long) &tramp[0], (unsigned long) &tramp[TRAMP_SIZE]); - return err; + return 0; + +efault: + return -EFAULT; } /* From 5dcf33085cd095010eecf42d04c9101821361d8d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 20 Oct 2026 10:05:40 -1000 Subject: [PATCH 47/48] powerpc/align: use get_user_instr() for a user-mode instruction fix_alignment() already reads the instruction with copy_inst_from_kernel_nofault() when the PC is a kernel address, and only uses __get_user_instr() when it is not. That pointer is an ordinary user address, so read it with get_user_instr(). On powerpc __get_user() already does might_fault() and opens user access for the read itself, so the only difference is the address check. This is part of getting rid of __get_user() and __put_user() entirely. Signed-off-by: Linus Torvalds --- arch/powerpc/kernel/align.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index 61409431138f..c66642cb211c 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c @@ -298,7 +298,7 @@ int fix_alignment(struct pt_regs *regs) if (is_kernel_addr(regs->nip)) r = copy_inst_from_kernel_nofault(&instr, (void *)regs->nip); else - r = __get_user_instr(instr, (void __user *)regs->nip); + r = get_user_instr(instr, (void __user *)regs->nip); if (unlikely(r)) return -EFAULT; From 14b482c98dfe9683b12338561a6e9bd44f7337a9 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 21 Oct 2026 07:13:59 -1000 Subject: [PATCH 48/48] drm/i915/gem: ignore errors on presumed_offset write-back It appears that the crazy "don't check for errors" code in the i915 relocation code was intentional, but not visible in the code as such. See commit 97a37c919f62 ("drm/i915/gem: Ignore readonly failures when updating relocs"). Quoting that commit: "Be damned if you do, and damned if you don't." So disable the error check again, but leave it around visibly for posterity as documentation for why it's not there. Fixes: 03c19e449ac6 ("drm/i915/gem: Don't fault on presumed_offset write-back") Reported-by: Rodrigo Vivi Signed-off-by: Linus Torvalds --- drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c index 8ed5aa3beae3..1b7f5bf13e50 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c @@ -1586,7 +1586,16 @@ static int eb_relocate_vma(struct i915_execbuffer *eb, struct eb_vma *ev) pagefault_disable(); err = put_user(offset, &urelocs[r - stack].presumed_offset); pagefault_enable(); - if (unlikely(err)) { + + /* + * Error handling is disabled on purpose. No, it makes + * no sense, and the code will loop over the relocations + * without writing them back and just waste CPU time, + * but that's apparently what the mprotect test-cases + * want. See commit 97a37c919f62 ("drm/i915/gem: Ignore + * readonly failures when updating relocs") + */ + if (unlikely(err) && 0) { remain = -EFAULT; goto out; }