summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/events/core.c41
-rw-r--r--kernel/events/uprobes.c39
-rw-r--r--kernel/fork.c1
3 files changed, 50 insertions, 31 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 1a73ba0747df..2eee83cdb43d 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7800,10 +7800,20 @@ unsigned long perf_misc_flags(struct perf_event *event,
unsigned long perf_instruction_pointer(struct perf_event *event,
struct pt_regs *regs)
{
- if (should_sample_guest(event))
- return perf_guest_get_ip();
+ /*
+ * Hardware skid can lead to a scenario where a PMI is
+ * delivered after the CPU has already entered kernel mode.
+ * In that case, user-space sampling must not expose kernel
+ * register state.
+ */
+ if (should_sample_guest(event)) {
+ return event->attr.exclude_kernel &&
+ !(perf_guest_state() & PERF_GUEST_USER) ?
+ 0 : perf_guest_get_ip();
+ }
- return perf_arch_instruction_pointer(regs);
+ return event->attr.exclude_kernel && !user_mode(regs) ?
+ 0 : perf_arch_instruction_pointer(regs);
}
static void
@@ -7837,10 +7847,22 @@ static void perf_sample_regs_user(struct perf_regs *regs_user,
}
static void perf_sample_regs_intr(struct perf_regs *regs_intr,
- struct pt_regs *regs)
+ struct pt_regs *regs,
+ bool exclude_kernel)
{
- regs_intr->regs = regs;
- regs_intr->abi = perf_reg_abi(current);
+ /*
+ * Hardware skid can lead to a scenario where a PMI is
+ * delivered after the CPU has already entered kernel mode.
+ * In that case, user-space sampling must not expose kernel
+ * register state.
+ */
+ if (exclude_kernel && !user_mode(regs)) {
+ regs_intr->abi = PERF_SAMPLE_REGS_ABI_NONE;
+ regs_intr->regs = NULL;
+ } else {
+ regs_intr->regs = regs;
+ regs_intr->abi = perf_reg_abi(current);
+ }
}
@@ -8731,7 +8753,8 @@ void perf_prepare_sample(struct perf_sample_data *data,
/* regs dump ABI info */
int size = sizeof(u64);
- perf_sample_regs_intr(&data->regs_intr, regs);
+ perf_sample_regs_intr(&data->regs_intr, regs,
+ event->attr.exclude_kernel);
if (data->regs_intr.regs) {
u64 mask = event->attr.sample_regs_intr;
@@ -13918,7 +13941,9 @@ SYSCALL_DEFINE5(perf_event_open,
if (err)
return err;
- if (!attr.exclude_kernel) {
+ if (!attr.exclude_kernel ||
+ ((attr.sample_type & PERF_SAMPLE_CALLCHAIN) &&
+ !attr.exclude_callchain_kernel)) {
err = perf_allow_kernel();
if (err)
return err;
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 6300b216012c..a18529ab2b87 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -54,7 +54,7 @@ static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ];
DEFINE_STATIC_PERCPU_RWSEM(dup_mmap_sem);
/* Covers return_instance's uprobe lifetime. */
-DEFINE_STATIC_SRCU(uretprobes_srcu);
+DEFINE_STATIC_SRCU_FAST_UPDOWN(uretprobes_srcu);
/* Have a copy of original instruction */
#define UPROBE_COPY_INSN 0
@@ -707,12 +707,13 @@ static void put_uprobe(struct uprobe *uprobe)
}
/* Initialize hprobe as SRCU-protected "leased" uprobe */
-static void hprobe_init_leased(struct hprobe *hprobe, struct uprobe *uprobe, int srcu_idx)
+static void hprobe_init_leased(struct hprobe *hprobe, struct uprobe *uprobe,
+ struct srcu_ctr __percpu *srcu_scp)
{
WARN_ON(!uprobe);
hprobe->state = HPROBE_LEASED;
hprobe->uprobe = uprobe;
- hprobe->srcu_idx = srcu_idx;
+ hprobe->srcu_scp = srcu_scp;
}
/* Initialize hprobe as refcounted ("stable") uprobe (uprobe can be NULL). */
@@ -720,7 +721,7 @@ static void hprobe_init_stable(struct hprobe *hprobe, struct uprobe *uprobe)
{
hprobe->state = uprobe ? HPROBE_STABLE : HPROBE_GONE;
hprobe->uprobe = uprobe;
- hprobe->srcu_idx = -1;
+ hprobe->srcu_scp = NULL;
}
/*
@@ -757,7 +758,7 @@ static void hprobe_finalize(struct hprobe *hprobe, enum hprobe_state hstate)
{
switch (hstate) {
case HPROBE_LEASED:
- __srcu_read_unlock(&uretprobes_srcu, hprobe->srcu_idx);
+ srcu_up_read_fast(&uretprobes_srcu, hprobe->srcu_scp);
break;
case HPROBE_STABLE:
put_uprobe(hprobe->uprobe);
@@ -829,7 +830,7 @@ static struct uprobe *hprobe_expire(struct hprobe *hprobe, bool get)
*/
if (try_cmpxchg(&hprobe->state, &hstate, uprobe ? HPROBE_STABLE : HPROBE_GONE)) {
/* We won the race, we are the ones to unlock SRCU */
- __srcu_read_unlock(&uretprobes_srcu, hprobe->srcu_idx);
+ srcu_up_read_fast(&uretprobes_srcu, hprobe->srcu_scp);
return get && uprobe ? get_uprobe(uprobe) : uprobe;
}
@@ -1806,14 +1807,6 @@ static struct xol_area *get_xol_area(void)
return area;
}
-void __weak arch_uprobe_clear_state(struct mm_struct *mm)
-{
-}
-
-void __weak arch_uprobe_init_state(struct mm_struct *mm)
-{
-}
-
/*
* uprobe_clear_state - Free the area allocated for slots.
*/
@@ -1825,8 +1818,6 @@ void uprobe_clear_state(struct mm_struct *mm)
delayed_uprobe_remove(NULL, mm);
mutex_unlock(&delayed_uprobe_lock);
- arch_uprobe_clear_state(mm);
-
if (!area)
return;
@@ -2045,7 +2036,7 @@ static void ri_timer(struct timer_list *timer)
struct return_instance *ri;
/* SRCU protects uprobe from reuse for the cmpxchg() inside hprobe_expire(). */
- guard(srcu)(&uretprobes_srcu);
+ guard(srcu_fast_updown)(&uretprobes_srcu);
/* RCU protects return_instance from freeing. */
guard(rcu)();
@@ -2142,7 +2133,7 @@ static int dup_utask(struct task_struct *t, struct uprobe_task *o_utask)
t->utask = n_utask;
/* protect uprobes from freeing, we'll need try_get_uprobe() them */
- guard(srcu)(&uretprobes_srcu);
+ guard(srcu_fast_updown)(&uretprobes_srcu);
p = &n_utask->return_instances;
for (o = o_utask->return_instances; o; o = o->next) {
@@ -2254,8 +2245,8 @@ static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs,
{
struct uprobe_task *utask = current->utask;
unsigned long orig_ret_vaddr, trampoline_vaddr;
+ struct srcu_ctr __percpu *srcu_scp;
bool chained;
- int srcu_idx;
if (!get_xol_area())
goto free;
@@ -2293,8 +2284,12 @@ static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs,
orig_ret_vaddr = utask->return_instances->orig_ret_vaddr;
}
- /* __srcu_read_lock() because SRCU lock survives switch to user space */
- srcu_idx = __srcu_read_lock(&uretprobes_srcu);
+ /*
+ * Use srcu_down_read_fast() because the SRCU lock survives a switch to
+ * user space and can be unlocked from a different context by ri_timer()
+ * or dup_utask().
+ */
+ srcu_scp = srcu_down_read_fast(&uretprobes_srcu);
ri->func = instruction_pointer(regs);
ri->stack = user_stack_pointer(regs);
@@ -2303,7 +2298,7 @@ static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs,
utask->depth++;
- hprobe_init_leased(&ri->hprobe, uprobe, srcu_idx);
+ hprobe_init_leased(&ri->hprobe, uprobe, srcu_scp);
ri->next = utask->return_instances;
rcu_assign_pointer(utask->return_instances, ri);
diff --git a/kernel/fork.c b/kernel/fork.c
index 94e021eabf1d..175c73bbe2bf 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1069,7 +1069,6 @@ static void mm_init_uprobes_state(struct mm_struct *mm)
{
#ifdef CONFIG_UPROBES
mm->uprobes_state.xol_area = NULL;
- arch_uprobe_init_state(mm);
#endif
}