| From d88d05a9e0b6d9356e97129d4ff9942d765f46ea Mon Sep 17 00:00:00 2001 |
| From: Kan Liang <kan.liang@linux.intel.com> |
| Date: Fri, 12 Mar 2021 05:21:37 -0800 |
| Subject: perf/x86/intel: Fix a crash caused by zero PEBS status |
| |
| From: Kan Liang <kan.liang@linux.intel.com> |
| |
| commit d88d05a9e0b6d9356e97129d4ff9942d765f46ea upstream. |
| |
| A repeatable crash can be triggered by the perf_fuzzer on some Haswell |
| system. |
| https://lore.kernel.org/lkml/7170d3b-c17f-1ded-52aa-cc6d9ae999f4@maine.edu/ |
| |
| For some old CPUs (HSW and earlier), the PEBS status in a PEBS record |
| may be mistakenly set to 0. To minimize the impact of the defect, the |
| commit was introduced to try to avoid dropping the PEBS record for some |
| cases. It adds a check in the intel_pmu_drain_pebs_nhm(), and updates |
| the local pebs_status accordingly. However, it doesn't correct the PEBS |
| status in the PEBS record, which may trigger the crash, especially for |
| the large PEBS. |
| |
| It's possible that all the PEBS records in a large PEBS have the PEBS |
| status 0. If so, the first get_next_pebs_record_by_bit() in the |
| __intel_pmu_pebs_event() returns NULL. The at = NULL. Since it's a large |
| PEBS, the 'count' parameter must > 1. The second |
| get_next_pebs_record_by_bit() will crash. |
| |
| Besides the local pebs_status, correct the PEBS status in the PEBS |
| record as well. |
| |
| Fixes: 01330d7288e0 ("perf/x86: Allow zero PEBS status with only single active event") |
| Reported-by: Vince Weaver <vincent.weaver@maine.edu> |
| Suggested-by: Peter Zijlstra (Intel) <peterz@infradead.org> |
| Signed-off-by: Kan Liang <kan.liang@linux.intel.com> |
| Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> |
| Cc: stable@vger.kernel.org |
| Link: https://lkml.kernel.org/r/1615555298-140216-1-git-send-email-kan.liang@linux.intel.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| arch/x86/events/intel/ds.c | 2 +- |
| 1 file changed, 1 insertion(+), 1 deletion(-) |
| |
| --- a/arch/x86/events/intel/ds.c |
| +++ b/arch/x86/events/intel/ds.c |
| @@ -1473,7 +1473,7 @@ static void intel_pmu_drain_pebs_nhm(str |
| */ |
| if (!pebs_status && cpuc->pebs_enabled && |
| !(cpuc->pebs_enabled & (cpuc->pebs_enabled-1))) |
| - pebs_status = cpuc->pebs_enabled; |
| + pebs_status = p->status = cpuc->pebs_enabled; |
| |
| bit = find_first_bit((unsigned long *)&pebs_status, |
| x86_pmu.max_pebs_events); |