From 0f697d307cab76512f504b3aa0aa5df0205fd50b Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Oct 2026 19:23:44 -1000 Subject: [PATCH] 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(); }