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

From: Falcon, Thomas

Date: Fri Jun 05 2026 - 15:08:34 EST


On Fri, 2026-06-05 at 09:11 +0800, Dapeng Mi wrote:
> 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,

Sorry to nitpick but it might be better to say "Hardware skid can lead
to a scenario where a PMI is delivered..."

Other than that, LGTM.

Reviewed-by: Thomas Falcon <thomas.falcon@xxxxxxxxx>

Thanks,
Tom

> + * 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;