Skip to content

Commit 91bd008

Browse files
committed
Merge tag 'probes-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace
Pull probes updates from Masami Hiramatsu: "Uprobes: - x86/shstk: Make return uprobe work with shadow stack - Add uretprobe syscall which speeds up the uretprobe 10-30% faster. This syscall is automatically used from user-space trampolines which are generated by the uretprobe. If this syscall is used by normal user program, it will cause SIGILL. Note that this is currently only implemented on x86_64. (This also has two fixes for adjusting the syscall number to avoid conflict with new *attrat syscalls.) - uprobes/perf: fix user stack traces in the presence of pending uretprobe. This corrects the uretprobe's trampoline address in the stacktrace with correct return address - selftests/x86: Add a return uprobe with shadow stack test - selftests/bpf: Add uretprobe syscall related tests. - test case for register integrity check - test case with register changing case - test case for uretprobe syscall without uprobes (expected to fail) - test case for uretprobe with shadow stack - selftests/bpf: add test validating uprobe/uretprobe stack traces - MAINTAINERS: Add uprobes entry. This does not specify the tree but to clarify who maintains and reviews the uprobes Kprobes: - tracing/kprobes: Test case cleanups. Replace redundant WARN_ON_ONCE() + pr_warn() with WARN_ONCE() and remove unnecessary code from selftest - tracing/kprobes: Add symbol counting check when module loads. This checks the uniqueness of the probed symbol on modules. The same check has already done for kernel symbols (This also has a fix for build error with CONFIG_MODULES=n) Cleanup: - Add MODULE_DESCRIPTION() macros for fprobe and kprobe examples" * tag 'probes-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: MAINTAINERS: Add uprobes entry selftests/bpf: Change uretprobe syscall number in uprobe_syscall test uprobe: Change uretprobe syscall scope and number tracing/kprobes: Fix build error when find_module() is not available tracing/kprobes: Add symbol counting check when module loads selftests/bpf: add test validating uprobe/uretprobe stack traces perf,uprobes: fix user stack traces in the presence of pending uretprobes tracing/kprobe: Remove cleanup code unrelated to selftest tracing/kprobe: Integrate test warnings into WARN_ONCE selftests/bpf: Add uretprobe shadow stack test selftests/bpf: Add uretprobe syscall call from user space test selftests/bpf: Add uretprobe syscall test for regs changes selftests/bpf: Add uretprobe syscall test for regs integrity selftests/x86: Add return uprobe shadow stack test uprobe: Add uretprobe syscall to speed up return probe uprobe: Wire up uretprobe system call x86/shstk: Make return uprobe work with shadow stack samples: kprobes: add missing MODULE_DESCRIPTION() macros fprobe: add missing MODULE_DESCRIPTION() macro
2 parents cb273eb + c26b1b8 commit 91bd008

File tree

23 files changed

+1320
-92
lines changed

23 files changed

+1320
-92
lines changed

MAINTAINERS

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23367,6 +23367,19 @@ F: drivers/mtd/ubi/
2336723367
F: include/linux/mtd/ubi.h
2336823368
F: include/uapi/mtd/ubi-user.h
2336923369

23370+
UPROBES
23371+
M: Masami Hiramatsu <[email protected]>
23372+
M: Oleg Nesterov <[email protected]>
23373+
M: Peter Zijlstra <[email protected]>
23374+
23375+
23376+
S: Maintained
23377+
F: arch/*/include/asm/uprobes.h
23378+
F: arch/*/kernel/probes/uprobes.c
23379+
F: arch/*/kernel/uprobes.c
23380+
F: include/linux/uprobes.h
23381+
F: kernel/events/uprobes.c
23382+
2337023383
USB "USBNET" DRIVER FRAMEWORK
2337123384
M: Oliver Neukum <[email protected]>
2337223385

arch/x86/entry/syscalls/syscall_64.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@
385385
460 common lsm_set_self_attr sys_lsm_set_self_attr
386386
461 common lsm_list_modules sys_lsm_list_modules
387387
462 common mseal sys_mseal
388+
467 common uretprobe sys_uretprobe
388389

389390
#
390391
# Due to a historical design error, certain syscalls are numbered differently

arch/x86/include/asm/shstk.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ unsigned long shstk_alloc_thread_stack(struct task_struct *p, unsigned long clon
2121
void shstk_free(struct task_struct *p);
2222
int setup_signal_shadow_stack(struct ksignal *ksig);
2323
int restore_signal_shadow_stack(void);
24+
int shstk_update_last_frame(unsigned long val);
25+
bool shstk_is_enabled(void);
2426
#else
2527
static inline long shstk_prctl(struct task_struct *task, int option,
2628
unsigned long arg2) { return -EINVAL; }
@@ -31,6 +33,8 @@ static inline unsigned long shstk_alloc_thread_stack(struct task_struct *p,
3133
static inline void shstk_free(struct task_struct *p) {}
3234
static inline int setup_signal_shadow_stack(struct ksignal *ksig) { return 0; }
3335
static inline int restore_signal_shadow_stack(void) { return 0; }
36+
static inline int shstk_update_last_frame(unsigned long val) { return 0; }
37+
static inline bool shstk_is_enabled(void) { return false; }
3438
#endif /* CONFIG_X86_USER_SHADOW_STACK */
3539

3640
#endif /* __ASSEMBLY__ */

arch/x86/kernel/shstk.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,3 +577,19 @@ long shstk_prctl(struct task_struct *task, int option, unsigned long arg2)
577577
return wrss_control(true);
578578
return -EINVAL;
579579
}
580+
581+
int shstk_update_last_frame(unsigned long val)
582+
{
583+
unsigned long ssp;
584+
585+
if (!features_enabled(ARCH_SHSTK_SHSTK))
586+
return 0;
587+
588+
ssp = get_user_shstk_addr();
589+
return write_user_shstk_64((u64 __user *)ssp, (u64)val);
590+
}
591+
592+
bool shstk_is_enabled(void)
593+
{
594+
return features_enabled(ARCH_SHSTK_SHSTK);
595+
}

arch/x86/kernel/uprobes.c

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/ptrace.h>
1313
#include <linux/uprobes.h>
1414
#include <linux/uaccess.h>
15+
#include <linux/syscalls.h>
1516

1617
#include <linux/kdebug.h>
1718
#include <asm/processor.h>
@@ -308,6 +309,122 @@ static int uprobe_init_insn(struct arch_uprobe *auprobe, struct insn *insn, bool
308309
}
309310

310311
#ifdef CONFIG_X86_64
312+
313+
asm (
314+
".pushsection .rodata\n"
315+
".global uretprobe_trampoline_entry\n"
316+
"uretprobe_trampoline_entry:\n"
317+
"pushq %rax\n"
318+
"pushq %rcx\n"
319+
"pushq %r11\n"
320+
"movq $" __stringify(__NR_uretprobe) ", %rax\n"
321+
"syscall\n"
322+
".global uretprobe_syscall_check\n"
323+
"uretprobe_syscall_check:\n"
324+
"popq %r11\n"
325+
"popq %rcx\n"
326+
327+
/* The uretprobe syscall replaces stored %rax value with final
328+
* return address, so we don't restore %rax in here and just
329+
* call ret.
330+
*/
331+
"retq\n"
332+
".global uretprobe_trampoline_end\n"
333+
"uretprobe_trampoline_end:\n"
334+
".popsection\n"
335+
);
336+
337+
extern u8 uretprobe_trampoline_entry[];
338+
extern u8 uretprobe_trampoline_end[];
339+
extern u8 uretprobe_syscall_check[];
340+
341+
void *arch_uprobe_trampoline(unsigned long *psize)
342+
{
343+
static uprobe_opcode_t insn = UPROBE_SWBP_INSN;
344+
struct pt_regs *regs = task_pt_regs(current);
345+
346+
/*
347+
* At the moment the uretprobe syscall trampoline is supported
348+
* only for native 64-bit process, the compat process still uses
349+
* standard breakpoint.
350+
*/
351+
if (user_64bit_mode(regs)) {
352+
*psize = uretprobe_trampoline_end - uretprobe_trampoline_entry;
353+
return uretprobe_trampoline_entry;
354+
}
355+
356+
*psize = UPROBE_SWBP_INSN_SIZE;
357+
return &insn;
358+
}
359+
360+
static unsigned long trampoline_check_ip(void)
361+
{
362+
unsigned long tramp = uprobe_get_trampoline_vaddr();
363+
364+
return tramp + (uretprobe_syscall_check - uretprobe_trampoline_entry);
365+
}
366+
367+
SYSCALL_DEFINE0(uretprobe)
368+
{
369+
struct pt_regs *regs = task_pt_regs(current);
370+
unsigned long err, ip, sp, r11_cx_ax[3];
371+
372+
if (regs->ip != trampoline_check_ip())
373+
goto sigill;
374+
375+
err = copy_from_user(r11_cx_ax, (void __user *)regs->sp, sizeof(r11_cx_ax));
376+
if (err)
377+
goto sigill;
378+
379+
/* expose the "right" values of r11/cx/ax/sp to uprobe_consumer/s */
380+
regs->r11 = r11_cx_ax[0];
381+
regs->cx = r11_cx_ax[1];
382+
regs->ax = r11_cx_ax[2];
383+
regs->sp += sizeof(r11_cx_ax);
384+
regs->orig_ax = -1;
385+
386+
ip = regs->ip;
387+
sp = regs->sp;
388+
389+
uprobe_handle_trampoline(regs);
390+
391+
/*
392+
* Some of the uprobe consumers has changed sp, we can do nothing,
393+
* just return via iret.
394+
* .. or shadow stack is enabled, in which case we need to skip
395+
* return through the user space stack address.
396+
*/
397+
if (regs->sp != sp || shstk_is_enabled())
398+
return regs->ax;
399+
regs->sp -= sizeof(r11_cx_ax);
400+
401+
/* for the case uprobe_consumer has changed r11/cx */
402+
r11_cx_ax[0] = regs->r11;
403+
r11_cx_ax[1] = regs->cx;
404+
405+
/*
406+
* ax register is passed through as return value, so we can use
407+
* its space on stack for ip value and jump to it through the
408+
* trampoline's ret instruction
409+
*/
410+
r11_cx_ax[2] = regs->ip;
411+
regs->ip = ip;
412+
413+
err = copy_to_user((void __user *)regs->sp, r11_cx_ax, sizeof(r11_cx_ax));
414+
if (err)
415+
goto sigill;
416+
417+
/* ensure sysret, see do_syscall_64() */
418+
regs->r11 = regs->flags;
419+
regs->cx = regs->ip;
420+
421+
return regs->ax;
422+
423+
sigill:
424+
force_sig(SIGILL);
425+
return -1;
426+
}
427+
311428
/*
312429
* If arch_uprobe->insn doesn't use rip-relative addressing, return
313430
* immediately. Otherwise, rewrite the instruction so that it accesses
@@ -1076,8 +1193,13 @@ arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs
10761193
return orig_ret_vaddr;
10771194

10781195
nleft = copy_to_user((void __user *)regs->sp, &trampoline_vaddr, rasize);
1079-
if (likely(!nleft))
1196+
if (likely(!nleft)) {
1197+
if (shstk_update_last_frame(trampoline_vaddr)) {
1198+
force_sig(SIGSEGV);
1199+
return -1;
1200+
}
10801201
return orig_ret_vaddr;
1202+
}
10811203

10821204
if (nleft != rasize) {
10831205
pr_err("return address clobbered: pid=%d, %%sp=%#lx, %%ip=%#lx\n",

include/linux/syscalls.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -979,6 +979,8 @@ asmlinkage long sys_lsm_list_modules(u64 __user *ids, u32 __user *size, u32 flag
979979
/* x86 */
980980
asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on);
981981

982+
asmlinkage long sys_uretprobe(void);
983+
982984
/* pciconfig: alpha, arm, arm64, ia64, sparc */
983985
asmlinkage long sys_pciconfig_read(unsigned long bus, unsigned long dfn,
984986
unsigned long off, unsigned long len,

include/linux/uprobes.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ extern bool arch_uretprobe_is_alive(struct return_instance *ret, enum rp_check c
138138
extern bool arch_uprobe_ignore(struct arch_uprobe *aup, struct pt_regs *regs);
139139
extern void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
140140
void *src, unsigned long len);
141+
extern void uprobe_handle_trampoline(struct pt_regs *regs);
142+
extern void *arch_uprobe_trampoline(unsigned long *psize);
143+
extern unsigned long uprobe_get_trampoline_vaddr(void);
141144
#else /* !CONFIG_UPROBES */
142145
struct uprobes_state {
143146
};

include/uapi/asm-generic/unistd.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -841,8 +841,11 @@ __SYSCALL(__NR_lsm_list_modules, sys_lsm_list_modules)
841841
#define __NR_mseal 462
842842
__SYSCALL(__NR_mseal, sys_mseal)
843843

844+
#define __NR_uretprobe 463
845+
__SYSCALL(__NR_uretprobe, sys_uretprobe)
846+
844847
#undef __NR_syscalls
845-
#define __NR_syscalls 463
848+
#define __NR_syscalls 464
846849

847850
/*
848851
* 32 bit systems traditionally used different

kernel/events/callchain.c

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/perf_event.h>
1212
#include <linux/slab.h>
1313
#include <linux/sched/task_stack.h>
14+
#include <linux/uprobes.h>
1415

1516
#include "internal.h"
1617

@@ -176,13 +177,51 @@ put_callchain_entry(int rctx)
176177
put_recursion_context(this_cpu_ptr(callchain_recursion), rctx);
177178
}
178179

180+
static void fixup_uretprobe_trampoline_entries(struct perf_callchain_entry *entry,
181+
int start_entry_idx)
182+
{
183+
#ifdef CONFIG_UPROBES
184+
struct uprobe_task *utask = current->utask;
185+
struct return_instance *ri;
186+
__u64 *cur_ip, *last_ip, tramp_addr;
187+
188+
if (likely(!utask || !utask->return_instances))
189+
return;
190+
191+
cur_ip = &entry->ip[start_entry_idx];
192+
last_ip = &entry->ip[entry->nr - 1];
193+
ri = utask->return_instances;
194+
tramp_addr = uprobe_get_trampoline_vaddr();
195+
196+
/*
197+
* If there are pending uretprobes for the current thread, they are
198+
* recorded in a list inside utask->return_instances; each such
199+
* pending uretprobe replaces traced user function's return address on
200+
* the stack, so when stack trace is captured, instead of seeing
201+
* actual function's return address, we'll have one or many uretprobe
202+
* trampoline addresses in the stack trace, which are not helpful and
203+
* misleading to users.
204+
* So here we go over the pending list of uretprobes, and each
205+
* encountered trampoline address is replaced with actual return
206+
* address.
207+
*/
208+
while (ri && cur_ip <= last_ip) {
209+
if (*cur_ip == tramp_addr) {
210+
*cur_ip = ri->orig_ret_vaddr;
211+
ri = ri->next;
212+
}
213+
cur_ip++;
214+
}
215+
#endif
216+
}
217+
179218
struct perf_callchain_entry *
180219
get_perf_callchain(struct pt_regs *regs, u32 init_nr, bool kernel, bool user,
181220
u32 max_stack, bool crosstask, bool add_mark)
182221
{
183222
struct perf_callchain_entry *entry;
184223
struct perf_callchain_entry_ctx ctx;
185-
int rctx;
224+
int rctx, start_entry_idx;
186225

187226
entry = get_callchain_entry(&rctx);
188227
if (!entry)
@@ -215,7 +254,9 @@ get_perf_callchain(struct pt_regs *regs, u32 init_nr, bool kernel, bool user,
215254
if (add_mark)
216255
perf_callchain_store_context(&ctx, PERF_CONTEXT_USER);
217256

257+
start_entry_idx = entry->nr;
218258
perf_callchain_user(&ctx, regs);
259+
fixup_uretprobe_trampoline_entries(entry, start_entry_idx);
219260
}
220261
}
221262

0 commit comments

Comments
 (0)