lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <c3d670cd-fc54-49a8-b640-fb16f9bd0487@arm.com>
Date: Wed, 11 Feb 2026 15:00:13 +0000
From: Christian Loehle <christian.loehle@....com>
To: Aboorva Devarajan <aboorvad@...ux.ibm.com>, rafael@...nel.org,
 daniel.lezcano@...aro.org
Cc: linux-pm@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH] cpuidle: ladder: Fix state index when only one idle state
 is registered

On 2/11/26 05:35, Aboorva Devarajan wrote:
> On certain platforms (PowerNV systems without a power-mgt DT node),
> cpuidle may register only a single idle state. In cases where that
> single state is a polling state (state 0), the ladder governor may
> incorrectly treat state 1 as the first usable state and pass an
> out-of-bounds index. This can lead to a NULL enter callback being
> invoked, ultimately resulting in a system crash.
> 
> [   13.342636] cpuidle-powernv : Only Snooze is available
> [   13.351854] Faulting instruction address: 0x00000000
> [   13.376489] NIP [0000000000000000] 0x0
> [   13.378351] LR  [c000000001e01974] cpuidle_enter_state+0x2c4/0x668
> 
> Fix this by determining the first non-polling state index based on
> the number of registered states, and by returning state 0 when only
> one state is registered.
> 
> Fixes: dc2251bf98c6 ("cpuidle: Eliminate the CPUIDLE_DRIVER_STATE_START symbol")
> Signed-off-by: Aboorva Devarajan <aboorvad@...ux.ibm.com>

Agreed that the current behavior is a bug, but is there really much value
in using a cpuidle governor with just a polling state?
It's dead code and trivial to bail out of in cpuidle, right?

> ---
>  drivers/cpuidle/governors/ladder.c | 24 ++++++++++++++++++++----
>  1 file changed, 20 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/cpuidle/governors/ladder.c b/drivers/cpuidle/governors/ladder.c
> index 6617eb494a11..294a688ed0bb 100644
> --- a/drivers/cpuidle/governors/ladder.c
> +++ b/drivers/cpuidle/governors/ladder.c
> @@ -42,6 +42,21 @@ struct ladder_device {
>  
>  static DEFINE_PER_CPU(struct ladder_device, ladder_devices);
>  
> +/**
> + * ladder_get_first_idx - get the first non-polling state index
> + * @drv: cpuidle driver
> + *
> + * Returns the index of the first non-polling state, or 0 if state 0 is not
> + * polling or if there's only one state available.
> + */
> +static inline int ladder_get_first_idx(struct cpuidle_driver *drv)
> +{
> +	if (drv->state_count > 1 &&
> +	    drv->states[0].flags & CPUIDLE_FLAG_POLLING)
> +		return 1;
> +	return 0;
> +}
> +
>  /**
>   * ladder_do_selection - prepares private data for a state change
>   * @dev: the CPU
> @@ -70,16 +85,17 @@ static int ladder_select_state(struct cpuidle_driver *drv,
>  	struct ladder_device *ldev = this_cpu_ptr(&ladder_devices);
>  	struct ladder_device_state *last_state;
>  	int last_idx = dev->last_state_idx;
> -	int first_idx = drv->states[0].flags & CPUIDLE_FLAG_POLLING ? 1 : 0;
> +	int first_idx;
>  	s64 latency_req = cpuidle_governor_latency_req(dev->cpu);
>  	s64 last_residency;
>  
> -	/* Special case when user has set very strict latency requirement */
> -	if (unlikely(latency_req == 0)) {
> +	/* Special case when there's only one state or strict latency requirement */
> +	if (unlikely(drv->state_count <= 1 || latency_req == 0)) {
>  		ladder_do_selection(dev, ldev, last_idx, 0);
>  		return 0;
>  	}
>  
> +	first_idx = ladder_get_first_idx(drv);
>  	last_state = &ldev->states[last_idx];
>  
>  	last_residency = dev->last_residency_ns - drv->states[last_idx].exit_latency_ns;
> @@ -134,7 +150,7 @@ static int ladder_enable_device(struct cpuidle_driver *drv,
>  				struct cpuidle_device *dev)
>  {
>  	int i;
> -	int first_idx = drv->states[0].flags & CPUIDLE_FLAG_POLLING ? 1 : 0;
> +	int first_idx = ladder_get_first_idx(drv);
>  	struct ladder_device *ldev = &per_cpu(ladder_devices, dev->cpu);
>  	struct ladder_device_state *lstate;
>  	struct cpuidle_state *state;


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ