Merge branch 'perf/urgent'

Pick up fixes and dependencies.

Signed-off-by: Peter Zijlstra <peterz@infradead.org>
This commit is contained in:
Peter Zijlstra 2026-07-03 16:30:34 +02:00
commit ded9b5bd03
3 changed files with 113 additions and 16 deletions

View File

@ -761,9 +761,9 @@ void arch_uprobe_clear_state(struct mm_struct *mm)
destroy_uprobe_trampoline(tramp);
}
static bool __in_uprobe_trampoline(unsigned long ip)
static bool __in_uprobe_trampoline(struct mm_struct *mm, unsigned long ip)
{
struct vm_area_struct *vma = vma_lookup(current->mm, ip);
struct vm_area_struct *vma = vma_lookup(mm, ip);
return vma && vma_is_special_mapping(vma, &tramp_mapping);
}
@ -776,14 +776,14 @@ static bool in_uprobe_trampoline(unsigned long ip)
rcu_read_lock();
if (mmap_lock_speculate_try_begin(mm, &seq)) {
found = __in_uprobe_trampoline(ip);
found = __in_uprobe_trampoline(mm, ip);
retry = mmap_lock_speculate_retry(mm, seq);
}
rcu_read_unlock();
if (retry) {
mmap_read_lock(mm);
found = __in_uprobe_trampoline(ip);
found = __in_uprobe_trampoline(mm, ip);
mmap_read_unlock(mm);
}
return found;
@ -1044,7 +1044,7 @@ static int copy_from_vaddr(struct mm_struct *mm, unsigned long vaddr, void *dst,
return 0;
}
static bool __is_optimized(uprobe_opcode_t *insn, unsigned long vaddr)
static bool __is_optimized(struct mm_struct *mm, uprobe_opcode_t *insn, unsigned long vaddr)
{
struct __packed __arch_relative_insn {
u8 op;
@ -1053,7 +1053,7 @@ static bool __is_optimized(uprobe_opcode_t *insn, unsigned long vaddr)
if (!is_call_insn(insn))
return false;
return __in_uprobe_trampoline(vaddr + 5 + call->raddr);
return __in_uprobe_trampoline(mm, vaddr + 5 + call->raddr);
}
static int is_optimized(struct mm_struct *mm, unsigned long vaddr)
@ -1064,7 +1064,7 @@ static int is_optimized(struct mm_struct *mm, unsigned long vaddr)
err = copy_from_vaddr(mm, vaddr, &insn, 5);
if (err)
return err;
return __is_optimized((uprobe_opcode_t *)&insn, vaddr);
return __is_optimized(mm, (uprobe_opcode_t *)&insn, vaddr);
}
static bool should_optimize(struct arch_uprobe *auprobe)
@ -1246,9 +1246,15 @@ static int default_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs
long correction = utask->vaddr - utask->xol_vaddr;
regs->ip += correction;
} else if (auprobe->defparam.fixups & UPROBE_FIX_CALL) {
unsigned long retaddr = utask->vaddr + auprobe->defparam.ilen;
int err;
regs->sp += sizeof_long(regs); /* Pop incorrect return address */
if (emulate_push_stack(regs, utask->vaddr + auprobe->defparam.ilen))
if (emulate_push_stack(regs, retaddr))
return -ERESTART;
err = shstk_update_last_frame(retaddr);
if (err)
return err;
}
/* popf; tell the caller to not touch TF */
if (auprobe->defparam.fixups & UPROBE_FIX_SETF)
@ -1338,6 +1344,10 @@ static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
*/
if (emulate_push_stack(regs, new_ip))
return false;
if (shstk_push(new_ip) == -EFAULT) {
regs->sp += sizeof_long(regs);
return false;
}
} else if (!check_jmp_cond(auprobe, regs)) {
offs = 0;
}

View File

@ -4729,7 +4729,7 @@ static void perf_remove_from_owner(struct perf_event *event);
static void perf_event_exit_event(struct perf_event *event,
struct perf_event_context *ctx,
struct task_struct *task,
bool revoke);
unsigned long detach_flags);
/*
* Removes all events from the current task that have been marked
@ -4756,7 +4756,7 @@ static void perf_event_remove_on_exec(struct perf_event_context *ctx)
modified = true;
perf_event_exit_event(event, ctx, ctx->task, false);
perf_event_exit_event(event, ctx, ctx->task, DETACH_GROUP);
}
raw_spin_lock_irqsave(&ctx->lock, flags);
@ -12960,7 +12960,7 @@ static void __pmu_detach_event(struct pmu *pmu, struct perf_event *event,
/*
* De-schedule the event and mark it REVOKED.
*/
perf_event_exit_event(event, ctx, ctx->task, true);
perf_event_exit_event(event, ctx, ctx->task, DETACH_REVOKE);
/*
* All _free_event() bits that rely on event->pmu:
@ -14550,12 +14550,13 @@ static void
perf_event_exit_event(struct perf_event *event,
struct perf_event_context *ctx,
struct task_struct *task,
bool revoke)
unsigned long detach_flags)
{
struct perf_event *parent_event = event->parent;
unsigned long detach_flags = DETACH_EXIT;
unsigned int attach_state;
detach_flags |= DETACH_EXIT;
if (parent_event) {
/*
* Do not destroy the 'original' grouping; because of the
@ -14578,8 +14579,8 @@ perf_event_exit_event(struct perf_event *event,
sync_child_event(event, task);
}
if (revoke)
detach_flags |= DETACH_GROUP | DETACH_REVOKE;
if (detach_flags & DETACH_REVOKE)
detach_flags |= DETACH_GROUP;
perf_remove_from_context(event, detach_flags);
/*
@ -14667,7 +14668,7 @@ static void perf_event_exit_task_context(struct task_struct *task, bool exit)
perf_event_task(task, ctx, 0);
list_for_each_entry_safe(child_event, next, &ctx->event_list, event_entry)
perf_event_exit_event(child_event, ctx, exit ? task : NULL, false);
perf_event_exit_event(child_event, ctx, exit ? task : NULL, 0);
mutex_unlock(&ctx->mutex);

View File

@ -873,6 +873,86 @@ static int test_uretprobe(void)
return err;
}
/* Keep the CALL first so the function address is exactly the probed CALL. */
extern void uprobe_call_trigger(void);
asm (".pushsection .text\n"
".global uprobe_call_target\n"
".type uprobe_call_target, @function\n"
"uprobe_call_target:\n"
" ret\n"
".size uprobe_call_target, .-uprobe_call_target\n"
".global uprobe_call_trigger\n"
".type uprobe_call_trigger, @function\n"
"uprobe_call_trigger:\n"
" call uprobe_call_target\n"
" ret\n"
".size uprobe_call_trigger, .-uprobe_call_trigger\n"
".popsection\n"
);
/* If CALL emulation misses the shadow stack update, this exits via SIGSEGV. */
static int test_uprobe_call(void)
{
const size_t attr_sz = sizeof(struct perf_event_attr);
const char *file = "/proc/self/exe";
int fd = -1, type, err = 1;
struct perf_event_attr attr;
struct sigaction sa = {};
ssize_t offset;
type = determine_uprobe_perf_type();
if (type < 0) {
if (type == -ENOENT)
printf("[SKIP]\tUprobe on CALL test, uprobes are not available\n");
return 0;
}
offset = get_uprobe_offset(uprobe_call_trigger);
if (offset < 0)
return 1;
sa.sa_sigaction = segv_gp_handler;
sa.sa_flags = SA_SIGINFO;
if (sigaction(SIGSEGV, &sa, NULL))
return 1;
/* Setup entry uprobe through perf event interface. */
memset(&attr, 0, attr_sz);
attr.size = attr_sz;
attr.type = type;
attr.config = 0;
attr.config1 = (__u64)(unsigned long)file;
attr.config2 = offset;
fd = syscall(__NR_perf_event_open, &attr, 0 /* pid */, -1 /* cpu */,
-1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
if (fd < 0)
goto out;
if (sigsetjmp(jmp_buffer, 1))
goto out;
if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK))
goto out;
/*
* This either segfaults and goes through sigsetjmp above
* or succeeds and we're good.
*/
uprobe_call_trigger();
printf("[OK]\tUprobe on CALL test\n");
err = 0;
out:
ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK);
signal(SIGSEGV, SIG_DFL);
if (fd >= 0)
close(fd);
return err;
}
void segv_handler_ptrace(int signum, siginfo_t *si, void *uc)
{
/* The SSP adjustment caused a segfault. */
@ -1071,6 +1151,12 @@ int main(int argc, char *argv[])
goto out;
}
if (test_uprobe_call()) {
ret = 1;
printf("[FAIL]\tuprobe on CALL test\n");
goto out;
}
return ret;
out: