From: sashiko-bot@kernel.org
To: "Qi Liu" <liuqi@hygon.cn>
Cc: linux-perf-users@vger.kernel.org
Subject: Re: [RFC PATCH 1/3] perf/x86/amd/uncore: Add common PMU helper functions
Date: Tue, 19 May 2026 04:07:36 +0000 [thread overview]
Message-ID: <20260519040736.A9206C2BCB3@smtp.kernel.org> (raw)
In-Reply-To: <20260519033225.1479907-2-liuqi@hygon.cn>
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] uncore_common_ctx_free() frees the PMU context without cancelling its hrtimer or verifying all events are removed, leading to UAF and NULL-pointer dereferences.
- [High] Unconditional double-stop of PMU events underflows ctx->nr_active, permanently breaking the hrtimer.
- [High] Unsanitized event->attr.config is written directly to hardware MSRs, enabling kernel panics and failing to stop counters.
- [Medium] Race condition during CPU context migration permanently disables the hrtimer on the destination CPU.
- [Low] Missing bounds check on pmu->num_counters can lead to an out-of-bounds write.
--
commit f16450b9a54bbeb570f328c8a8626473a456f6b8
Author: Qi Liu <liuqi@hygon.cn>
perf/x86/amd/uncore: Add common PMU helper functions
Add common helper functions for AMD-family uncore PMU handling.
The helpers cover event initialization, counter allocation, counter
read/update, event start/stop and per-CPU context management.
> diff --git a/arch/x86/events/amd/uncore_common.c b/arch/x86/events/amd/uncore_common.c
> new file mode 100644
> index 0000000000000..a6d50fe803df3
> --- /dev/null
> +++ b/arch/x86/events/amd/uncore_common.c
[ ... ]
> +void uncore_common_stop(struct perf_event *event, int flags)
> +{
> + struct uncore_common_pmu *pmu = event_to_uncore_common_pmu(event);
> + struct uncore_common_ctx *ctx = *per_cpu_ptr(pmu->ctx, event->cpu);
> + struct hw_perf_event *hwc = &event->hw;
> +
> + wrmsrq(hwc->config_base, hwc->config);
> + hwc->state |= PERF_HES_STOPPED;
> +
> + if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
> + event->pmu->read(event);
> + hwc->state |= PERF_HES_UPTODATE;
> + }
> +
> + if (!--ctx->nr_active)
> + uncore_common_cancel_hrtimer(ctx);
When a perf event is closed, free_event() calls pmu->del(), which maps to
uncore_common_del(). Since uncore_common_del() unconditionally calls
event->pmu->stop(), and the event was likely already stopped by
event_sched_out(), could this cause ctx->nr_active to underflow (e.g., from
0 to -1) because there is no check if the event is already stopped?
If ctx->nr_active underflows, a subsequent event start will evaluate
!ctx->nr_active++ as false (!-1 is 0), which would fail to call
uncore_common_start_hrtimer(), causing the hardware counters to silently
overflow.
> +
> + __clear_bit(hwc->idx, ctx->active_mask);
> +}
> +
> +int uncore_common_event_init(struct perf_event *event)
> +{
> + struct uncore_common_pmu *pmu;
> + struct uncore_common_ctx *ctx;
> + struct hw_perf_event *hwc = &event->hw;
> +
> + if (event->attr.type != event->pmu->type)
> + return -ENOENT;
> +
> + if (event->cpu < 0)
> + return -EINVAL;
> +
> + pmu = event_to_uncore_common_pmu(event);
> + ctx = *per_cpu_ptr(pmu->ctx, event->cpu);
> + if (!ctx)
> + return -ENODEV;
> +
> + hwc->config = event->attr.config;
Is it safe to copy the user-supplied event->attr.config directly to
hwc->config without any sanitization or bit masking?
If a user configures an event with reserved hardware bits set, could the
later call to wrmsrq() trigger a General Protection Fault and crash the
kernel?
Additionally, if the user explicitly sets the hardware enable bit (e.g.,
ARCH_PERFMON_EVENTSEL_ENABLE), wouldn't uncore_common_stop() fail to
actually stop the counter when it writes this configuration back to the MSR?
> + hwc->idx = -1;
> +
> + event->cpu = ctx->cpu;
> +
> + return 0;
> +}
> +
> +int uncore_common_add(struct perf_event *event, int flags)
> +{
> + struct uncore_common_pmu *pmu = event_to_uncore_common_pmu(event);
> + struct uncore_common_ctx *ctx = *per_cpu_ptr(pmu->ctx, event->cpu);
> + struct hw_perf_event *hwc = &event->hw;
> + int i;
> +
> + /* are we already assigned? */
> + if (hwc->idx != -1 && ctx->events[hwc->idx] == event)
> + goto out;
> +
> + for (i = 0; i < pmu->num_counters; i++) {
Is there a bounds check to ensure pmu->num_counters does not exceed
NUM_COUNTERS_MAX?
ctx->active_mask is sized based on NUM_COUNTERS_MAX. If a PMU is initialized
with more than 64 counters, couldn't a later __set_bit(hwc->idx,
ctx->active_mask) write out of bounds and corrupt adjacent memory?
> + if (ctx->events[i] == event) {
> + hwc->idx = i;
> + goto out;
> + }
> + }
[ ... ]
> +void uncore_common_ctx_free(struct uncore_common *uncore, unsigned int cpu)
> +{
> + struct uncore_common_pmu *pmu;
> + struct uncore_common_ctx *ctx;
> + int i;
> +
> + if (!uncore->init_done)
> + return;
> +
> + for (i = 0; i < uncore->num_pmus; i++) {
> + pmu = &uncore->pmus[i];
> +
> + if (!pmu->ctx)
> + continue;
> +
> + ctx = *per_cpu_ptr(pmu->ctx, cpu);
> + if (!ctx)
> + continue;
> +
> + if (cpu == ctx->cpu)
> + cpumask_clear_cpu(cpu, &pmu->active_mask);
> +
> + if (!--ctx->refcnt) {
> + kfree(ctx->events);
> + kfree(ctx);
> + }
When the last CPU in the PMU's domain goes offline, ctx->refcnt will drop
to 0 and ctx is freed. However, is there anything ensuring hrtimer_cancel()
is called if there are still active events attached?
If the hrtimer is still running, it will execute on the freed ctx memory.
> +
> + *per_cpu_ptr(pmu->ctx, cpu) = NULL;
Additionally, since the user's perf_event file descriptors remain valid,
when the user later closes them, uncore_common_del() will attempt to access
the context.
Since *per_cpu_ptr(pmu->ctx, cpu) is set to NULL here, won't that result
in a NULL-pointer dereference?
> + }
> +}
> +
> +void uncore_common_ctx_move(struct uncore_common *uncore, unsigned int cpu)
> +{
> + struct uncore_common_ctx *curr, *next;
> + struct uncore_common_pmu *pmu;
> + int i, j;
[ ... ]
> + for_each_online_cpu(j) {
> + if (cpu == j)
> + continue;
> +
> + next = *per_cpu_ptr(pmu->ctx, j);
> + if (!next)
> + continue;
> +
> + if (curr == next) {
> + perf_pmu_migrate_context(&pmu->pmu, cpu, j);
> + cpumask_clear_cpu(cpu, &pmu->active_mask);
> + cpumask_set_cpu(j, &pmu->active_mask);
> + next->cpu = j;
Should next->cpu = j be set before calling perf_pmu_migrate_context()?
perf_pmu_migrate_context() synchronously installs and starts events on the
destination CPU, which arms the hrtimer. If the hrtimer expires before the
migration thread reaches next->cpu = j, uncore_common_hrtimer() will
observe that ctx->cpu != smp_processor_id() and return HRTIMER_NORESTART.
This would permanently disable the PMU timer on the new CPU.
> + break;
> + }
> + }
> + }
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260519033225.1479907-1-liuqi@hygon.cn?part=1
next prev parent reply other threads:[~2026-05-19 4:07 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-19 3:32 [RFC PATCH 0/3] perf/x86/amd/uncore: Add common helpers and Hygon support Qi Liu
2026-05-19 3:32 ` [RFC PATCH 1/3] perf/x86/amd/uncore: Add common PMU helper functions Qi Liu
2026-05-19 4:07 ` sashiko-bot [this message]
2026-05-19 3:32 ` [RFC PATCH 2/3] perf/x86/amd/uncore: Convert AMD driver to common PMU helpers Qi Liu
2026-05-19 4:37 ` sashiko-bot
2026-05-19 3:32 ` [RFC PATCH 3/3] perf/x86/amd/uncore: Add Hygon uncore PMU support Qi Liu
2026-05-19 5:11 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260519040736.A9206C2BCB3@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=liuqi@hygon.cn \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox