[Acked] [PATCH 1/1] arm64: perf: reject groups spanning multiple HW PMUs

Andy Whitcroft apw at canonical.com
Fri Jun 9 13:43:45 UTC 2017


On Fri, Jun 09, 2017 at 05:56:41AM -0700, Brad Figg wrote:
> From: "Suzuki K. Poulose" <suzuki.poulose at arm.com>
> 
> CVE-2015-8955
> 
> The perf core implicitly rejects events spanning multiple HW PMUs, as in
> these cases the event->ctx will differ. However this validation is
> performed after pmu::event_init() is called in perf_init_event(), and
> thus pmu::event_init() may be called with a group leader from a
> different HW PMU.
> 
> The ARM64 PMU driver does not take this fact into account, and when
> validating groups assumes that it can call to_arm_pmu(event->pmu) for
> any HW event. When the event in question is from another HW PMU this is
> wrong, and results in dereferencing garbage.
> 
> This patch updates the ARM64 PMU driver to first test for and reject
> events from other PMUs, moving the to_arm_pmu and related logic after
> this test. Fixes a crash triggered by perf_fuzzer on Linux-4.0-rc2, with
> a CCI PMU present:
> 
> Bad mode in Synchronous Abort handler detected, code 0x86000006 -- IABT (current EL)
> CPU: 0 PID: 1371 Comm: perf_fuzzer Not tainted 3.19.0+ #249
> Hardware name: V2F-1XV7 Cortex-A53x2 SMM (DT)
> task: ffffffc07c73a280 ti: ffffffc07b0a0000 task.ti: ffffffc07b0a0000
> PC is at 0x0
> LR is at validate_event+0x90/0xa8
> pc : [<0000000000000000>] lr : [<ffffffc000090228>] pstate: 00000145
> sp : ffffffc07b0a3ba0
> 
> [<          (null)>]           (null)
> [<ffffffc0000907d8>] armpmu_event_init+0x174/0x3cc
> [<ffffffc00015d870>] perf_try_init_event+0x34/0x70
> [<ffffffc000164094>] perf_init_event+0xe0/0x10c
> [<ffffffc000164348>] perf_event_alloc+0x288/0x358
> [<ffffffc000164c5c>] SyS_perf_event_open+0x464/0x98c
> Code: bad PC value
> 
> Also cleans up the code to use the arm_pmu only when we know
> that we are dealing with an arm pmu event.
> 
> Cc: Will Deacon <will.deacon at arm.com>
> Acked-by: Mark Rutland <mark.rutland at arm.com>
> Acked-by: Peter Ziljstra (Intel) <peterz at infradead.org>
> Signed-off-by: Suzuki K. Poulose <suzuki.poulose at arm.com>
> Signed-off-by: Will Deacon <will.deacon at arm.com>
> (cherry picked from commit 8fff105e13041e49b82f92eef034f363a6b1c071)
> Signed-off-by: Brad Figg <brad.figg at canonical.com>
> ---
>  arch/arm64/kernel/perf_event.c | 21 +++++++++++++++------
>  1 file changed, 15 insertions(+), 6 deletions(-)
> 
> diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
> index 5b1cd79..da7af1f9 100644
> --- a/arch/arm64/kernel/perf_event.c
> +++ b/arch/arm64/kernel/perf_event.c
> @@ -316,22 +316,31 @@ out:
>  }
>  
>  static int
> -validate_event(struct pmu_hw_events *hw_events,
> -	       struct perf_event *event)
> +validate_event(struct pmu *pmu, struct pmu_hw_events *hw_events,
> +				struct perf_event *event)
>  {
> -	struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
> +	struct arm_pmu *armpmu;
>  	struct hw_perf_event fake_event = event->hw;
>  	struct pmu *leader_pmu = event->group_leader->pmu;
>  
>  	if (is_software_event(event))
>  		return 1;
>  
> +	/*
> +	 * Reject groups spanning multiple HW PMUs (e.g. CPU + CCI). The
> +	 * core perf code won't check that the pmu->ctx == leader->ctx
> +	 * until after pmu->event_init(event).
> +	 */
> +	if (event->pmu != pmu)
> +		return 0;
> +
>  	if (event->pmu != leader_pmu || event->state < PERF_EVENT_STATE_OFF)
>  		return 1;
>  
>  	if (event->state == PERF_EVENT_STATE_OFF && !event->attr.enable_on_exec)
>  		return 1;
>  
> +	armpmu = to_arm_pmu(event->pmu);
>  	return armpmu->get_event_idx(hw_events, &fake_event) >= 0;
>  }
>  
> @@ -349,15 +358,15 @@ validate_group(struct perf_event *event)
>  	memset(fake_used_mask, 0, sizeof(fake_used_mask));
>  	fake_pmu.used_mask = fake_used_mask;
>  
> -	if (!validate_event(&fake_pmu, leader))
> +	if (!validate_event(event->pmu, &fake_pmu, leader))
>  		return -EINVAL;
>  
>  	list_for_each_entry(sibling, &leader->sibling_list, group_entry) {
> -		if (!validate_event(&fake_pmu, sibling))
> +		if (!validate_event(event->pmu, &fake_pmu, sibling))
>  			return -EINVAL;
>  	}
>  
> -	if (!validate_event(&fake_pmu, event))
> +	if (!validate_event(event->pmu, &fake_pmu, event))
>  		return -EINVAL;
>  
>  	return 0;

Clean cherry-pick.  Looks to do what is claimed.  Therefore: 

Acked-by: Andy Whitcroft <apw at canonical.com>

-apw




More information about the kernel-team mailing list