[Patch v5 17/18] perf/x86/intel: Pass LBR stack size into store_lbr()

From: Dapeng Mi

Date: Thu Oct 29 2026 - 00:56:24 EST


intel_pmu_store_lbr() assumes the LBR stack always contains
x86_pmu.lbr_nr entries, but arch-PEBS can use a smaller configured depth.
This can result in out-of-bounds reads when the stack is shallower than
the hardware maximum.

Add a lbr_nr parameter to the helper so the code only processes the
available entries and avoids overrunning the LBR buffer.

Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
Fixes: d21954c8a0ff ("perf/x86/intel: Process arch-PEBS records or record fragments")
Signed-off-by: Dapeng Mi <dapeng1.mi@xxxxxxxxxxxxxxx>
---
arch/x86/events/intel/ds.c | 8 ++++----
arch/x86/events/intel/lbr.c | 16 +++++++++-------
arch/x86/events/perf_event.h | 2 +-
3 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index 8444670cee4a..ac452083de7a 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -2591,12 +2591,12 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event,

if (format_group & PEBS_DATACFG_LBRS) {
struct lbr_entry *lbr = next_record;
- int num_lbr = ((format_group >> PEBS_DATACFG_LBR_SHIFT)
+ unsigned int num_lbr = ((format_group >> PEBS_DATACFG_LBR_SHIFT)
& 0xff) + 1;
next_record = next_record + num_lbr * sizeof(struct lbr_entry);

if (has_branch_stack(event)) {
- intel_pmu_store_pebs_lbrs(lbr);
+ intel_pmu_store_pebs_lbrs(lbr, num_lbr);
intel_pmu_lbr_save_brstack(data, cpuc, event);
}
}
@@ -2777,7 +2777,7 @@ static void setup_arch_pebs_sample_data(struct perf_event *event,
if (header->lbr) {
struct arch_pebs_lbr_header *lbr_header = next_record;
struct lbr_entry *lbr;
- int num_lbr;
+ unsigned int num_lbr;

next_record = lbr_header + 1;
lbr = next_record;
@@ -2788,7 +2788,7 @@ static void setup_arch_pebs_sample_data(struct perf_event *event,
next_record += num_lbr * sizeof(struct lbr_entry);

if (has_branch_stack(event)) {
- intel_pmu_store_pebs_lbrs(lbr);
+ intel_pmu_store_pebs_lbrs(lbr, num_lbr);
intel_pmu_lbr_save_brstack(data, cpuc, event);
}
}
diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c
index e9336d570242..f0098b0f16d3 100644
--- a/arch/x86/events/intel/lbr.c
+++ b/arch/x86/events/intel/lbr.c
@@ -911,7 +911,7 @@ static __always_inline u16 get_lbr_cycles(u64 info)

static_assert((64 - PERF_BRANCH_ENTRY_INFO_BITS_MAX) > LBR_INFO_BR_CNTR_NUM * LBR_INFO_BR_CNTR_BITS);

-static void intel_pmu_store_lbr(struct lbr_entry *entries,
+static void intel_pmu_store_lbr(struct lbr_entry *entries, int lbr_nr,
struct perf_branch_stack *lbr_stack,
struct perf_branch_entry *lbr_entries)
{
@@ -921,7 +921,7 @@ static void intel_pmu_store_lbr(struct lbr_entry *entries,
bool mispred;
int i;

- for (i = 0; i < x86_pmu.lbr_nr; i++) {
+ for (i = 0; i < lbr_nr; i++) {
lbr = entries ? &entries[i] : NULL;
e = &lbr_entries[i];

@@ -1027,7 +1027,7 @@ static void intel_pmu_arch_lbr_read(struct cpu_hw_events *cpuc,
struct perf_branch_stack *lbr_stack,
struct perf_branch_entry *lbr_entries)
{
- intel_pmu_store_lbr(NULL, lbr_stack, lbr_entries);
+ intel_pmu_store_lbr(NULL, x86_pmu.lbr_nr, lbr_stack, lbr_entries);
}

static void
@@ -1038,14 +1038,15 @@ intel_pmu_arch_lbr_read_xsave(struct cpu_hw_events *cpuc,
struct x86_perf_task_context_arch_lbr_xsave *xsave = cpuc->lbr_xsave;

if (!xsave) {
- intel_pmu_store_lbr(NULL, lbr_stack, lbr_entries);
+ intel_pmu_store_lbr(NULL, x86_pmu.lbr_nr, lbr_stack, lbr_entries);
return;
}
xsave->header.xfeatures = 0;
xsaves(&xsave->xsave, XFEATURE_MASK_LBR);

if (xsave->header.xfeatures & XFEATURE_MASK_LBR) {
- intel_pmu_store_lbr(xsave->lbr.entries, lbr_stack, lbr_entries);
+ intel_pmu_store_lbr(xsave->lbr.entries, x86_pmu.lbr_nr,
+ lbr_stack, lbr_entries);
} else {
/* Clear the LBR stack to avoid leaking stale entries. */
lbr_stack->nr = 0;
@@ -1323,7 +1324,7 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc,
}
}

-void intel_pmu_store_pebs_lbrs(struct lbr_entry *lbr)
+void intel_pmu_store_pebs_lbrs(struct lbr_entry *lbr, unsigned int lbr_nr)
{
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);

@@ -1334,7 +1335,8 @@ void intel_pmu_store_pebs_lbrs(struct lbr_entry *lbr)
else
cpuc->lbr_stack.hw_idx = intel_pmu_lbr_tos();

- intel_pmu_store_lbr(lbr, &cpuc->lbr_stack, cpuc->lbr_entries);
+ intel_pmu_store_lbr(lbr, min(lbr_nr, x86_pmu.lbr_nr),
+ &cpuc->lbr_stack, cpuc->lbr_entries);
intel_pmu_lbr_filter(cpuc, &cpuc->lbr_stack,
cpuc->lbr_entries, cpuc->lbr_counters);
}
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 160df7b99621..feceeebf0d06 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -1967,7 +1967,7 @@ void intel_pmu_pebs_late_setup(struct cpu_hw_events *cpuc);

void intel_pmu_drain_pebs_buffer(void);

-void intel_pmu_store_pebs_lbrs(struct lbr_entry *lbr);
+void intel_pmu_store_pebs_lbrs(struct lbr_entry *lbr, unsigned int lbr_nr);

void intel_pebs_init(void);

--
2.34.1