perf/x86: Remove stale fixed counter helper and fix hybrid PMU access

On hybrid systems, init_hw_perf_events() can call check_hw_exists() with
the global PMU pointer after perf_is_hybrid is set. In that case,
fixed_counter_disabled() uses hybrid() on a non-hybrid PMU object, so the
intel_ctrl access is taken from the wrong layout and can read out of
bounds.

fixed_counter_disabled() was added in commit 32451614da
("perf/x86/intel: Support CPUID 10.ECX to disable fixed counters"), when
fixed counters were tracked via num_fixed_counters. Today fixed counters
are represented by fixed_cntr_mask, so this helper is obsolete.

Remove fixed_counter_disabled() and its callers, and rely directly on the
fixed-counter bitmask. With the helper gone, check_hw_exists() no longer
needs a PMU argument, so drop that parameter as well. This removes the
invalid hybrid access and closes the out-of-bounds read risk.

Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
Reviewed-by: Zide Chen <zide.chen@intel.com>
Link: https://patch.msgid.link/20260717080342.1879573-6-dapeng1.mi@linux.intel.com
This commit is contained in:
Dapeng Mi 2026-07-17 16:03:39 +08:00 committed by Peter Zijlstra
parent 85182f902a
commit 3ba87c5bf1
3 changed files with 4 additions and 17 deletions

View File

@ -265,7 +265,7 @@ static void release_pmc_hardware(void) {}
#endif
bool check_hw_exists(struct pmu *pmu, unsigned long *cntr_mask,
bool check_hw_exists(unsigned long *cntr_mask,
unsigned long *fixed_cntr_mask)
{
u64 val, val_fail = -1, val_new= ~0;
@ -297,8 +297,6 @@ bool check_hw_exists(struct pmu *pmu, unsigned long *cntr_mask,
if (ret)
goto msr_fail;
for_each_set_bit(i, fixed_cntr_mask, X86_PMC_IDX_MAX) {
if (fixed_counter_disabled(i, pmu))
continue;
if (val & (0x03ULL << i*4)) {
bios_fail = 1;
val_fail = val;
@ -1618,8 +1616,6 @@ void perf_event_print_debug(void)
cpu, idx, prev_left);
}
for_each_set_bit(idx, fixed_cntr_mask, X86_PMC_IDX_MAX) {
if (fixed_counter_disabled(idx, cpuc->pmu))
continue;
rdmsrq(x86_pmu_fixed_ctr_addr(idx), pmc_count);
pr_info("CPU#%d: fixed-PMC%d count: %016llx\n",
@ -2180,7 +2176,7 @@ static int __init init_hw_perf_events(void)
pmu_check_apic();
/* sanity check that the hardware exists or is emulated */
if (!check_hw_exists(&pmu, x86_pmu.cntr_mask, x86_pmu.fixed_cntr_mask))
if (!check_hw_exists(x86_pmu.cntr_mask, x86_pmu.fixed_cntr_mask))
goto out_bad_pmu;
pr_cont("%s PMU driver.\n", x86_pmu.name);

View File

@ -3713,8 +3713,6 @@ static void intel_pmu_reset(void)
wrmsrq_safe(x86_pmu_event_addr(idx), 0ull);
}
for_each_set_bit(idx, fixed_cntr_mask, INTEL_PMC_MAX_FIXED) {
if (fixed_counter_disabled(idx, cpuc->pmu))
continue;
wrmsrq_safe(x86_pmu_fixed_ctr_addr(idx), 0ull);
}
@ -6336,7 +6334,7 @@ static bool init_hybrid_pmu(int cpu)
intel_pmu_check_hybrid_pmus(pmu);
if (!check_hw_exists(&pmu->pmu, pmu->cntr_mask, pmu->fixed_cntr_mask)) {
if (!check_hw_exists(pmu->cntr_mask, pmu->fixed_cntr_mask)) {
cpuc->pmu = NULL;
return false;
}

View File

@ -1243,7 +1243,7 @@ static inline int x86_pmu_rdpmc_index(int index)
return x86_pmu.rdpmc_index ? x86_pmu.rdpmc_index(index) : index;
}
bool check_hw_exists(struct pmu *pmu, unsigned long *cntr_mask,
bool check_hw_exists(unsigned long *cntr_mask,
unsigned long *fixed_cntr_mask);
int x86_add_exclusive(unsigned int what);
@ -1456,13 +1456,6 @@ ssize_t events_hybrid_sysfs_show(struct device *dev,
struct device_attribute *attr,
char *page);
static inline bool fixed_counter_disabled(int i, struct pmu *pmu)
{
u64 intel_ctrl = hybrid(pmu, intel_ctrl);
return !(intel_ctrl >> (i + INTEL_PMC_IDX_FIXED));
}
#ifdef CONFIG_CPU_SUP_AMD
int amd_pmu_init(void);