Linux Perf Users
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Sasha Levin" <sashal@kernel.org>
Cc: linux-perf-users@vger.kernel.org
Subject: Re: [PATCH AUTOSEL 6.18-6.12] RISC-V: perf: fix resource cleanup on driver probe failure
Date: Mon, 19 Oct 2026 15:33:18 +0000	[thread overview]
Message-ID: <sashiko-outbox-177756@kernel.org> (raw)
In-Reply-To: <20261019135441.1420968-443-sashal@kernel.org>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] perf: riscv: NULL deref in riscv_pmu_get_hpm_info() after probe failure
- [High] riscv: perf: incomplete snapshot and IRQ cleanup on probe failure
- [Medium] perf: riscv: leak sysctl table registration on pmu_sbi_device_probe() failure
--

commit 45f193117e60b41831289f45f79fab573ecae630
Author: Atish Patra <atishp@meta.com>

RISC-V: perf: fix resource cleanup on driver probe failure

This commit reorganizes the error handling in pmu_sbi_device_probe(),
pmu_sbi_setup_irqs(), and riscv_pmu_destroy() to clean up registered
PMU instances, per-CPU IRQs, pmu_ctr_list, and CPU hotplug state on
probe failure.

Link: https://patch.msgid.link/20260807-counter_delegation-v9-1-58658104e487@meta.com

> diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
> index 7dd282da67cef..e794a40522fe6 100644
> --- a/drivers/perf/riscv_pmu_sbi.c
> +++ b/drivers/perf/riscv_pmu_sbi.c

[ ... ]

> @@ -1515,9 +1525,20 @@ static int pmu_sbi_device_probe(struct platform_device *pdev)
>  	return 0;
>  
>  out_unregister:
> +	perf_pmu_unregister(&pmu->pmu);

[Severity: Medium]
When cpuhp_state_add_instance() fails and jumps to out_unregister, does
this path leak the sysctl table header registered just beforehand in
pmu_sbi_device_probe()?

drivers/perf/riscv_pmu_sbi.c:pmu_sbi_device_probe() {
	...
	register_sysctl("kernel", sbi_pmu_sysctl_table);

	ret = cpuhp_state_add_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node);
	if (ret)
		goto out_unregister;
}

Because the ctl_table_header pointer returned by register_sysctl() is not
saved or passed to unregister_sysctl_table() in out_unregister, the sysctl
registration remains active after probe failure.

> +
> +out_destroy:
>  	riscv_pmu_destroy(pmu);
> +	if (irq_requested) {
> +		free_percpu_irq(riscv_pmu_irq, pmu->hw_events);
> +		irq_dispose_mapping(riscv_pmu_irq);
> +		riscv_pmu_irq = 0;
> +	}

[Severity: High]
If cpuhp_state_add_instance() fails on a secondary CPU during
pmu_sbi_starting_cpu(), can this cleanup leave the per-CPU IRQ enabled,
snapshot shared memory registered with SBI firmware, and the
sbi_pmu_snapshot_available static branch enabled?

First, in pmu_sbi_starting_cpu(), the per-CPU IRQ is enabled before
setting up the snapshot shared memory:

drivers/perf/riscv_pmu_sbi.c:pmu_sbi_starting_cpu() {
	...
	if (riscv_pmu_use_irq) {
		cpu_hw_evt->irq = riscv_pmu_irq;
		ALT_SBI_PMU_OVF_CLEAR_PENDING(riscv_pmu_irq_mask);
		enable_percpu_irq(riscv_pmu_irq, IRQ_TYPE_NONE);
	}

	if (sbi_pmu_snapshot_available())
		return pmu_sbi_snapshot_setup(pmu, cpu);
	return 0;
}

If pmu_sbi_snapshot_setup() fails on that CPU, pmu_sbi_starting_cpu()
returns an error with riscv_pmu_irq still enabled on the failing CPU, and
cpuhp_rollback_install() only invokes pmu_sbi_dying_cpu() on earlier CPUs.
When out_destroy then calls free_percpu_irq(riscv_pmu_irq, pmu->hw_events),
__free_percpu_irq() sees that desc->percpu_enabled is not empty, triggers a
warning, and returns without removing the irqaction before out_free calls
free_percpu(pmu->hw_events).

Second, pmu_sbi_device_probe() sets up the snapshot shared memory on the
initial probing CPU and enables the static key before calling
cpuhp_state_add_instance():

drivers/perf/riscv_pmu_sbi.c:pmu_sbi_device_probe() {
	...
		cpu = get_cpu();
		ret = pmu_sbi_snapshot_setup(pmu, cpu);
		put_cpu();

		if (ret) {
			/* Snapshot is an optional feature. Continue if not available */
			pmu_sbi_snapshot_free(pmu);
		} else {
			pr_info("SBI PMU snapshot detected\n");
			static_branch_enable(&sbi_pmu_snapshot_available);
		}
}

In the error path, perf_pmu_unregister() can sleep and migrate the task to
another CPU before riscv_pmu_destroy() calls pmu_sbi_snapshot_disable() on
only the current CPU and frees the pages via pmu_sbi_snapshot_free(pmu). If
the initial probing CPU was not rolled back by cpuhp_state_add_instance()
(or if the task migrated), its freed physical page remains registered with
SBI firmware. In addition, riscv_pmu_destroy() does not call
static_branch_disable(&sbi_pmu_snapshot_available).

>  
>  out_free:
> +	free_percpu(pmu->hw_events);
> +	kfree(pmu_ctr_list);
> +	pmu_ctr_list = NULL;
>  	kfree(pmu);
>  	return ret;
>  }

[Severity: High]
Can freeing pmu_ctr_list and setting it to NULL here without clearing the
static global cmask cause a NULL pointer dereference in
riscv_pmu_get_hpm_info()?

In pmu_sbi_device_probe(), pmu_sbi_get_ctrinfo(num_counters, &cmask)
populates the file-scope cmask bitmask alongside allocating pmu_ctr_list.
If a later step in pmu_sbi_device_probe() fails, out_free frees
pmu_ctr_list and sets it to NULL, while cmask stays non-zero.

When kvm_riscv_vcpu_pmu_init() subsequently calls riscv_pmu_get_hpm_info():

drivers/perf/riscv_pmu_sbi.c:riscv_pmu_get_hpm_info() {
	...
	if (!cmask)
		return -EINVAL;

	for_each_set_bit(i, &cmask, RISCV_MAX_COUNTERS) {
		info = &pmu_ctr_list[i];
		if (!info)
			continue;
		if (!hpm_width && info->csr != CSR_CYCLE && info->csr != CSR_INSTRET)
			hpm_width = info->width;
		if (info->type == SBI_PMU_CTR_TYPE_HW)
			hpm_count++;
	}
	...
}

Because cmask is non-zero, &pmu_ctr_list[i] for any set bit i > 0 evaluates
to a non-NULL offset from NULL, bypassing the !info check and dereferencing
info->csr and info->type. Should out_free also reset cmask = 0?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261019135441.1420968-443-sashal@kernel.org?part=1

  reply	other threads:[~2026-10-19 15:33 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20261019135441.1420968-1-sashal@kernel.org>
2026-10-19 13:45 ` [PATCH AUTOSEL 6.18-5.15] perf/x86/intel/uncore: Fix PCI PMU cleanup on setup failure Sasha Levin
2026-10-19 14:02   ` sashiko-bot
2026-10-19 13:45 ` [PATCH AUTOSEL 6.18-6.1] perf/x86: Free hybrid state on PMU init failure Sasha Levin
2026-10-19 13:47 ` [PATCH AUTOSEL 6.18] perf/arm-cmn: Add workarounds for CMN-S3 on Graviton5 Sasha Levin
2026-10-19 14:35   ` sashiko-bot
2026-10-20 14:37   ` Aviv Bakal
2026-10-19 13:47 ` [PATCH AUTOSEL 6.18-6.1] perf: marvell: Cancel CN10K DDR PMU hrtimer on device remove Sasha Levin
2026-10-19 14:44   ` sashiko-bot
2026-10-19 13:48 ` [PATCH AUTOSEL 6.18] perf/core: Fix kernel register info leak via hardware skid Sasha Levin
2026-10-19 14:42   ` sashiko-bot
2026-10-19 13:51 ` [PATCH AUTOSEL 6.18-6.1] perf/x86: Unregister PMI handler on PMU init failure Sasha Levin
2026-10-19 15:17   ` sashiko-bot
2026-10-19 13:52 ` [PATCH AUTOSEL 6.18-6.12] RISC-V: perf: fix resource cleanup on driver probe failure Sasha Levin
2026-10-19 15:33   ` sashiko-bot [this message]
2026-10-19 13:52 ` [PATCH AUTOSEL 6.18-6.1] perf/x86/intel: Fallback to sw branch type decoding if no hw decoding Sasha Levin
2026-10-19 15:25   ` 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=sashiko-outbox-177756@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=sashal@kernel.org \
    --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