[PATCH 8/8] perf/core: Fix kernel register info leak via hardware skid

From: Dapeng Mi

Date: Thu Jun 04 2026 - 21:36:26 EST


An unprivileged hardware perf event using exclude_kernel=1 can leak kernel
register data to user space via PERF_SAMPLE_REGS_INTR. Due to hardware
skid, a PMI may trigger after the CPU has already entered kernel space
(Ring 0), bypassing the perf_allow_kernel() privilege barrier.

This security vulnerability is severely exacerbated by upcoming support
for SIMD register sampling via XSAVES, which could expose sensitive kernel
FPU states (such as active cryptographic keys).

Fix this by ensuring that sampled register data is dropped if the event's
exclude_kernel attribute is set but the PMI catches the CPU in kernel mode.

Link: https://lore.kernel.org/all/20260529085613.CCAFB1F00893@xxxxxxxxxxxxxxx/
Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
Cc: Mark Rutland <mark.rutland@xxxxxxx>
Signed-off-by: Dapeng Mi <dapeng1.mi@xxxxxxxxxxxxxxx>
---
kernel/events/core.c | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/kernel/events/core.c b/kernel/events/core.c
index 7935d5663944..b7326bc3acd0 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7800,10 +7800,21 @@ static void perf_sample_regs_user(struct perf_regs *regs_user,
}

static void perf_sample_regs_intr(struct perf_regs *regs_intr,
- struct pt_regs *regs)
+ struct pt_regs *regs,
+ bool exclude_kernel)
{
- regs_intr->regs = regs;
- regs_intr->abi = perf_reg_abi(current);
+ /*
+ * Hardware skid can lead to PMI is delivered after
+ * the CPU has already entered kernel mode. In that case,
+ * user-space sampling must not expose kernel register state.
+ */
+ if (exclude_kernel && !user_mode(regs)) {
+ regs_intr->abi = PERF_SAMPLE_REGS_ABI_NONE;
+ regs_intr->regs = NULL;
+ } else {
+ regs_intr->regs = regs;
+ regs_intr->abi = perf_reg_abi(current);
+ }
}


@@ -8694,7 +8705,8 @@ void perf_prepare_sample(struct perf_sample_data *data,
/* regs dump ABI info */
int size = sizeof(u64);

- perf_sample_regs_intr(&data->regs_intr, regs);
+ perf_sample_regs_intr(&data->regs_intr, regs,
+ event->attr.exclude_kernel);

if (data->regs_intr.regs) {
u64 mask = event->attr.sample_regs_intr;
--
2.34.1