Re: System crash with perf_fuzzer (kernel: 5.0.0-rc3)

From: Jiri Olsa
Date: Wed Jan 30 2019 - 13:36:57 EST


On Fri, Jan 25, 2019 at 12:16:27PM +0530, Ravi Bangoria wrote:

SNIP

> [ 1432.176374] general protection fault: 0000 [#1] SMP PTI
> [ 1432.182253] CPU: 1 PID: 0 Comm: swapper/1 Tainted: G W 5.0.0-rc3-ravi-pfuzzer+ #1
> [ 1432.192088] Hardware name: IBM CPU PLANAR -[8722xyz]-/00FL808, BIOS -[KOE162DUS-2.30]- 08/27/2018 [264/488]
> [ 1432.206120] RIP: 0010:perf_prepare_sample+0x8f/0x510
> [ 1432.211679] Code: ff ff 41 f6 c4 01 0f 85 22 02 00 00 41 f6 c4 20 74 26 4d 85 e4 0f 88 0c 01 00 00 4c 89 f6 4c 89 ff e8 f5 fe ff ff 49 89 45 70 <48> 8b 00 8d 04 c5 08 00 00 0
> 0 66 01 43 06 41 f7 c4 00 04 00 00 74
> [ 1432.232699] RSP: 0000:ffff95b3ff843a78 EFLAGS: 00010082
> [ 1432.238548] RAX: 8d1217eb826cce00 RBX: ffff95b3ff843ad8 RCX: 000000000000001f
> [ 1432.246536] RDX: 0000000000000000 RSI: 00000000355563e5 RDI: 0000000000000000
> [ 1432.254522] RBP: ffff95b3ff843ab0 R08: ffffd016493f3b42 R09: 0000000000000000
> [ 1432.262508] R10: ffff95b3ff843a08 R11: 0000000000000000 R12: 80000000000306e5
> [ 1432.270495] R13: ffff95b3ff843bc0 R14: ffff95b3ff843b18 R15: ffff95b3f6b65800
> [ 1432.278483] FS: 0000000000000000(0000) GS:ffff95b3ff840000(0000) knlGS:0000000000000000
> [ 1432.287539] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> [ 1432.293969] CR2: 000055bf7f768c90 CR3: 0000001fd220e005 CR4: 00000000000606e0
> [ 1432.301956] Call Trace:
> [ 1432.304697] <IRQ>
> [ 1432.306956] ? intel_pmu_drain_bts_buffer+0x194/0x230
> [ 1432.312615] intel_pmu_drain_bts_buffer+0x160/0x230
> [ 1432.318078] ? tick_nohz_irq_exit+0x31/0x40
> [ 1432.322765] ? smp_call_function_single_interrupt+0x48/0xe0
> [ 1432.328993] ? call_function_single_interrupt+0xf/0x20
> [ 1432.334745] ? call_function_single_interrupt+0xa/0x20
> [ 1432.340501] ? x86_schedule_events+0x1a0/0x2f0
> [ 1432.345475] ? x86_pmu_commit_txn+0xb4/0x100
> [ 1432.350258] ? find_busiest_group+0x47/0x5d0
> [ 1432.355039] ? perf_event_set_state.part.42+0x12/0x50
> [ 1432.360694] ? perf_mux_hrtimer_restart+0x40/0xb0
> [ 1432.365960] intel_pmu_disable_event+0xae/0x100
> [ 1432.371031] ? intel_pmu_disable_event+0xae/0x100
> [ 1432.376297] x86_pmu_stop+0x7a/0xb0
> [ 1432.380201] x86_pmu_del+0x57/0x120
> [ 1432.384105] event_sched_out.isra.101+0x83/0x180
> [ 1432.389272] group_sched_out.part.103+0x57/0xe0
> [ 1432.394343] ctx_sched_out+0x188/0x240
> [ 1432.398539] ctx_resched+0xa8/0xd0
> [ 1432.402345] __perf_event_enable+0x193/0x1e0
> [ 1432.407125] event_function+0x8e/0xc0
> [ 1432.411222] remote_function+0x41/0x50
> [ 1432.415418] flush_smp_call_function_queue+0x68/0x100
> [ 1432.421071] generic_smp_call_function_single_interrupt+0x13/0x30
> [ 1432.427893] smp_call_function_single_interrupt+0x3e/0xe0
> [ 1432.433936] call_function_single_interrupt+0xf/0x20
> [ 1432.440204] </IRQ>

hi,
I finaly managed to reproduced this one ;-)

also I found reproducer for crash on my server, now I'm running
the fuzzer to see if it cures it as well.. so far so good ;-)

the crash happens when I create 'almost bts' event with:
event = branch-instructions:up
sample_period = 2
sample_type = CALLCHAIN

after I open the event I change the sample_period via ioctl to '1',
which will make the event 'full bts', but no bts checks are performed
so the bts drain crashes

the patch adds check_eriod pmu callback.. I need to check if there's
better way to do this, but so far it fixes the crash for me

if you guys could check this patch, that'd be great

thanks,
jirka


---
arch/x86/events/core.c | 6 ++++++
arch/x86/events/intel/core.c | 9 +++++++++
arch/x86/events/perf_event.h | 2 ++
include/linux/perf_event.h | 5 +++++
kernel/events/core.c | 31 +++++++++++++++++++++++++++++++
5 files changed, 53 insertions(+)

diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 374a19712e20..e5db4fad517e 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -2278,6 +2278,11 @@ void perf_check_microcode(void)
x86_pmu.check_microcode();
}

+static int x86_pmu_check_period(struct perf_event *event)
+{
+ return x86_pmu.check_period ? x86_pmu.check_period(event) : 0;
+}
+
static struct pmu pmu = {
.pmu_enable = x86_pmu_enable,
.pmu_disable = x86_pmu_disable,
@@ -2302,6 +2307,7 @@ static struct pmu pmu = {
.event_idx = x86_pmu_event_idx,
.sched_task = x86_pmu_sched_task,
.task_ctx_size = sizeof(struct x86_perf_task_context),
+ .check_period = x86_pmu_check_period,
};

void arch_perf_update_userpage(struct perf_event *event,
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 40e12cfc87f6..125930e328c8 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -3584,6 +3584,11 @@ static void intel_pmu_sched_task(struct perf_event_context *ctx,
intel_pmu_lbr_sched_task(ctx, sched_in);
}

+static int intel_pmu_check_period(struct perf_event *event)
+{
+ return intel_pmu_has_bts(event) ? -EINVAL : 0;
+}
+
PMU_FORMAT_ATTR(offcore_rsp, "config1:0-63");

PMU_FORMAT_ATTR(ldlat, "config1:0-15");
@@ -3663,6 +3668,8 @@ static __initconst const struct x86_pmu core_pmu = {
.cpu_prepare = intel_pmu_cpu_prepare,
.cpu_starting = intel_pmu_cpu_starting,
.cpu_dying = intel_pmu_cpu_dying,
+
+ .check_period = intel_pmu_check_period,
};

static struct attribute *intel_pmu_attrs[];
@@ -3705,6 +3712,8 @@ static __initconst const struct x86_pmu intel_pmu = {
.cpu_dying = intel_pmu_cpu_dying,
.guest_get_msrs = intel_guest_get_msrs,
.sched_task = intel_pmu_sched_task,
+
+ .check_period = intel_pmu_check_period,
};

static __init void intel_clovertown_quirk(void)
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 78d7b7031bfc..170b58d48710 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -646,6 +646,8 @@ struct x86_pmu {
* Intel host/guest support (KVM)
*/
struct perf_guest_switch_msr *(*guest_get_msrs)(int *nr);
+
+ int (*check_period) (struct perf_event *event);
};

struct x86_perf_task_context {
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index a79e59fc3b7d..749a5c3110e2 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -447,6 +447,11 @@ struct pmu {
* Filter events for PMU-specific reasons.
*/
int (*filter_match) (struct perf_event *event); /* optional */
+
+ /*
+ * Check period for PERF_EVENT_IOC_PERIOD ioctl.
+ */
+ int (*check_period) (struct perf_event *event); /* optional */
};

enum perf_addr_filter_action_t {
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 280a72b3a553..22ec63a0782e 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -4969,6 +4969,26 @@ static void __perf_event_period(struct perf_event *event,
}
}

+static int check_period(struct perf_event *event, u64 value)
+{
+ u64 sample_period_attr = event->attr.sample_period;
+ u64 sample_period_hw = event->hw.sample_period;
+ int ret;
+
+ if (event->attr.freq) {
+ event->attr.sample_freq = value;
+ } else {
+ event->attr.sample_period = value;
+ event->hw.sample_period = value;
+ }
+
+ ret = event->pmu->check_period(event);
+
+ event->attr.sample_period = sample_period_attr;
+ event->hw.sample_period = sample_period_hw;
+ return ret;
+}
+
static int perf_event_period(struct perf_event *event, u64 __user *arg)
{
u64 value;
@@ -4985,6 +5005,9 @@ static int perf_event_period(struct perf_event *event, u64 __user *arg)
if (event->attr.freq && value > sysctl_perf_event_sample_rate)
return -EINVAL;

+ if (check_period(event, value))
+ return -EINVAL;
+
event_function_call(event, __perf_event_period, &value);

return 0;
@@ -9601,6 +9624,11 @@ static int perf_pmu_nop_int(struct pmu *pmu)
return 0;
}

+static int perf_event_nop_int(struct perf_event *event)
+{
+ return 0;
+}
+
static DEFINE_PER_CPU(unsigned int, nop_txn_flags);

static void perf_pmu_start_txn(struct pmu *pmu, unsigned int flags)
@@ -9901,6 +9929,9 @@ int perf_pmu_register(struct pmu *pmu, const char *name, int type)
pmu->pmu_disable = perf_pmu_nop_void;
}

+ if (!pmu->check_period)
+ pmu->check_period = perf_event_nop_int;
+
if (!pmu->event_idx)
pmu->event_idx = perf_event_idx_default;

--
2.17.2