Re: [PATCH v3 3/9] perf/cxl: Fix the counter overflow delta fixup

[email protected] Fri, 31 Jul 2026 23:37:24 +0000
Newsgroups org.kernel.vger.linux-cxl,org.kernel.vger.linux-perf-users
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 2 potential issue(=
s) to consider:

Pre-existing issues:
- [Medium] A race condition between a polled read and the overflow IRQ hand=
ler causes a double-counting of a full period if the polled read executes a=
fter the counter wraps but before the IRQ handler.
- [Medium] Missing validation for `info->counter_width` allows a non-compli=
ant or buggy CXL device to trigger Undefined Behavior when `GENMASK_ULL` is=
 evaluated.
--

--- Patch [3]: [PATCH v3 3/9] perf/cxl: Fix the counter overflow delta fixu=
p ---
commit 333d6be6b59d292e876b5c4888ab33d0abc8cc03
Author: Dave Jiang <[email protected]>

perf/cxl: Fix the counter overflow delta fixup

This commit alters the CXL PMU counter overflow logic to condition the fixup
on new_cnt >=3D prev_cnt, aiming to correctly handle cases where the subtra=
ction
cannot express the span over a period boundary, while accommodating events
that increment by more than 1 per cycle. It also modifies the period additi=
on
to use mask + 1 to avoid undefined behavior on 64-bit counters.

Link: https://sashiko.dev/#/patchset/20260715191454.459673-1-dave@stgolabs.=
net?part=3D1

> diff --git a/drivers/perf/cxl_pmu.c b/drivers/perf/cxl_pmu.c
> index b16e2e4090a39..3511d049aea40 100644
> --- a/drivers/perf/cxl_pmu.c
> +++ b/drivers/perf/cxl_pmu.c
> @@ -689,7 +689,7 @@ static void __cxl_pmu_read(struct perf_event *event, =
bool overflow)
>  {
>  	struct cxl_pmu_info *info =3D pmu_to_cxl_pmu_info(event->pmu);
>  	struct hw_perf_event *hwc =3D &event->hw;
> -	u64 new_cnt, prev_cnt, delta;
> +	u64 new_cnt, prev_cnt, delta, mask;
> =20
>  	do {
>  		prev_cnt =3D local64_read(&hwc->prev_count);

[ ... ]

>  	/*
> -	 * If we know an overflow occur then take that into account.
> -	 * Note counter is not reset as that would lose events
> +	 * The mask discards exactly the bit that says the counter wrapped, so a
> +	 * delta of one whole period reads back as 0, the same as no events at
> +	 * all. Only the overflow status distinguishes them, and new_cnt >=3D
> +	 * prev_cnt is that case, so add the period back. mask + 1 is
> +	 * 2^counter_width, which is 0 for a 64-bit counter - the right value to
> +	 * add once the top bit is gone, and no undefined 1 << 64.
>  	 */
> -	delta =3D (new_cnt - prev_cnt) & GENMASK_ULL(info->counter_width - 1, 0=
);
> -	if (overflow && delta < GENMASK_ULL(info->counter_width - 1, 0))
> -		delta +=3D (1UL << info->counter_width);
> +	mask =3D GENMASK_ULL(info->counter_width - 1, 0);

[Severity: Medium]
This is a pre-existing issue, but could a buggy or malicious CXL device
reporting a counter width of 0 (or > 64) trigger Undefined Behavior here?

If the device reports 0 for the counter width in the capability registers,
the driver reads this into info->counter_width without bounds validation.

When GENMASK_ULL(info->counter_width - 1, 0) is evaluated, it results in a
negative shift amount which leads to Undefined Behavior and corrupted PMU
masks.

> +	delta =3D (new_cnt - prev_cnt) & mask;
> +	if (overflow && new_cnt >=3D prev_cnt)
> +		delta +=3D mask + 1;

[Severity: Medium]
This isn't a bug introduced by this patch, but does this sequence allow a=20
race condition between a polled read and the overflow IRQ handler to
double-count a full period?

If the hardware counter wraps, setting the overflow bit, a polled read
(such as via perf stat -I) might read the counter before the hardware IRQ
is delivered.

The polled read would compute the delta, correctly including the period
because new_cnt is wrapped and smaller than prev_cnt, and then update
prev_count to the small post-wrap value.

When the delayed overflow IRQ handler subsequently runs, it reads the
overflow bit and calls __cxl_pmu_read(event, true). Because prev_cnt is
now the small post-wrap value, new_cnt >=3D prev_cnt evaluates to true.

The handler would then execute delta +=3D mask + 1, incorrectly adding a
second full period because it assumes the wrap wasn't already consumed.

> =20
>  	local64_add(delta, &event->count);
>  }

--=20
Sashiko AI review =C2=B7 https://sashiko.dev/#/patchset/20260731232827.4014=
[email protected]?part=3D3