linux-pm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] cpufreq: acpi-cpufreq: add NULL check for acpi_perf_data before freeing
@ 2026-08-10  6:10 lirongqing
  2026-08-10  6:10 ` [PATCH] cpufreq: acpi-cpufreq: Initialize cmd.val in drv_read() lirongqing
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: lirongqing @ 2026-08-10  6:10 UTC (permalink / raw)
  To: Rafael J . Wysocki, Viresh Kumar, linux-pm; +Cc: Li RongQing

From: Li RongQing <lirongqing@baidu.com>

If alloc_percpu() fails in acpi_cpufreq_early_init(), acpi_perf_data is
NULL.  Calling free_acpi_perf_data() will execute per_cpu_ptr(NULL, i),
which does not return NULL but an offset pointer.
Dereferencing ->shared_cpu_map on this invalid pointer leads to a kernel
crash.

Fix these issues by adding a NULL check at the start of
free_acpi_perf_data() and setting acpi_perf_data to NULL after freeing.

Signed-off-by: Li RongQing <lirongqing@baidu.com>
---
 drivers/cpufreq/acpi-cpufreq.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
index 87e4923..cd2ca87 100644
--- a/drivers/cpufreq/acpi-cpufreq.c
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -517,11 +517,14 @@ static void free_acpi_perf_data(void)
 {
 	unsigned int i;
 
-	/* Freeing a NULL pointer is OK, and alloc_percpu zeroes. */
+	if (!acpi_perf_data)
+		return;
+
 	for_each_possible_cpu(i)
 		free_cpumask_var(per_cpu_ptr(acpi_perf_data, i)
 				 ->shared_cpu_map);
 	free_percpu(acpi_perf_data);
+	acpi_perf_data = NULL;
 }
 
 static int cpufreq_boost_down_prep(unsigned int cpu)
-- 
2.9.4


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH] cpufreq: acpi-cpufreq: Initialize cmd.val in drv_read()
  2026-08-10  6:10 [PATCH] cpufreq: acpi-cpufreq: add NULL check for acpi_perf_data before freeing lirongqing
@ 2026-08-10  6:10 ` lirongqing
  2026-08-10 12:15   ` Zhongqiu Han
  2026-08-10  6:10 ` [PATCH] cpufreq: acpi-cpufreq: Using cpufreq_for_each_entry() to iterate in extract_io() lirongqing
  2026-08-10 11:21 ` [PATCH] cpufreq: acpi-cpufreq: add NULL check for acpi_perf_data before freeing Zhongqiu Han
  2 siblings, 1 reply; 8+ messages in thread
From: lirongqing @ 2026-08-10  6:10 UTC (permalink / raw)
  To: Rafael J . Wysocki, Viresh Kumar, linux-pm; +Cc: Li RongQing

From: Li RongQing <lirongqing@baidu.com>

In drv_read(), struct drv_cmd cmd is allocated on the stack with .val left
uninitialized. If smp_call_function_any() fails, do_drv_read() will not
run to populate cmd.val. As a result, drv_read() would return uninitialized
stack data.

Fix this by explicitly initializing .val to 0 when declaring cmd.

Signed-off-by: Li RongQing <lirongqing@baidu.com>
---
 drivers/cpufreq/acpi-cpufreq.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
index cd2ca87..6c7ece9 100644
--- a/drivers/cpufreq/acpi-cpufreq.c
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -308,6 +308,7 @@ static u32 drv_read(struct acpi_cpufreq_data *data, const struct cpumask *mask)
 	struct acpi_processor_performance *perf = to_perf_data(data);
 	struct drv_cmd cmd = {
 		.reg = &perf->control_register,
+		.val = 0,
 		.func.read = data->cpu_freq_read,
 	};
 	int err;
-- 
2.9.4


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH] cpufreq: acpi-cpufreq: Using cpufreq_for_each_entry() to iterate in extract_io()
  2026-08-10  6:10 [PATCH] cpufreq: acpi-cpufreq: add NULL check for acpi_perf_data before freeing lirongqing
  2026-08-10  6:10 ` [PATCH] cpufreq: acpi-cpufreq: Initialize cmd.val in drv_read() lirongqing
@ 2026-08-10  6:10 ` lirongqing
  2026-08-12 13:59   ` Zhongqiu Han
  2026-08-10 11:21 ` [PATCH] cpufreq: acpi-cpufreq: add NULL check for acpi_perf_data before freeing Zhongqiu Han
  2 siblings, 1 reply; 8+ messages in thread
From: lirongqing @ 2026-08-10  6:10 UTC (permalink / raw)
  To: Rafael J . Wysocki, Viresh Kumar, linux-pm; +Cc: Li RongQing

From: Li RongQing <lirongqing@baidu.com>

In extract_io(), the loop iterates up to perf->state_count. However,
when building policy->freq_table in acpi_cpufreq_cpu_init(), duplicate
frequency entries are skipped, making freq_table smaller than
perf->state_count.

Iterating perf->state_count times directly over policy->freq_table[i] can
result in out-of-bounds array reads. Furthermore, policy->freq_table[i]
does not necessarily correspond to perf->states[i], as the original P-state
index is stored in freq_table[entry].driver_data.

Fix this by using cpufreq_for_each_entry() to iterate over
policy->freq_table, similar to extract_msr().

Signed-off-by: Li RongQing <lirongqing@baidu.com>
---
 drivers/cpufreq/acpi-cpufreq.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
index 21639d9..87e4923 100644
--- a/drivers/cpufreq/acpi-cpufreq.c
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -196,15 +196,14 @@ static int check_amd_hwpstate_cpu(unsigned int cpuid)
 static unsigned extract_io(struct cpufreq_policy *policy, u32 value)
 {
 	struct acpi_cpufreq_data *data = policy->driver_data;
+	struct cpufreq_frequency_table *pos;
 	struct acpi_processor_performance *perf;
-	int i;
 
 	perf = to_perf_data(data);
 
-	for (i = 0; i < perf->state_count; i++) {
-		if (value == perf->states[i].status)
-			return policy->freq_table[i].frequency;
-	}
+	cpufreq_for_each_entry(pos, policy->freq_table)
+		if (value == perf->states[pos->driver_data].status)
+			return pos->frequency;
 	return 0;
 }
 
-- 
2.9.4


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH] cpufreq: acpi-cpufreq: add NULL check for acpi_perf_data before freeing
  2026-08-10  6:10 [PATCH] cpufreq: acpi-cpufreq: add NULL check for acpi_perf_data before freeing lirongqing
  2026-08-10  6:10 ` [PATCH] cpufreq: acpi-cpufreq: Initialize cmd.val in drv_read() lirongqing
  2026-08-10  6:10 ` [PATCH] cpufreq: acpi-cpufreq: Using cpufreq_for_each_entry() to iterate in extract_io() lirongqing
@ 2026-08-10 11:21 ` Zhongqiu Han
  2026-08-10 11:52   ` Zhongqiu Han
  2026-08-12 12:06   ` 答复: [外部邮件] " Li,Rongqing
  2 siblings, 2 replies; 8+ messages in thread
From: Zhongqiu Han @ 2026-08-10 11:21 UTC (permalink / raw)
  To: lirongqing, Rafael J . Wysocki, Viresh Kumar, linux-pm; +Cc: zhongqiu.han

On 8/10/2026 2:10 PM, lirongqing wrote:
> From: Li RongQing <lirongqing@baidu.com>
> 
> If alloc_percpu() fails in acpi_cpufreq_early_init(), acpi_perf_data is
> NULL.  Calling free_acpi_perf_data() will execute per_cpu_ptr(NULL, i),
> which does not return NULL but an offset pointer.

Hi RongQing,

Could you please confirm if acpi_perf_data() can ever be executed when 
alloc_percpu() fails?


> Dereferencing ->shared_cpu_map on this invalid pointer leads to a kernel
> crash.
> 
> Fix these issues by adding a NULL check at the start of
> free_acpi_perf_data() and setting acpi_perf_data to NULL after freeing.
> 
> Signed-off-by: Li RongQing <lirongqing@baidu.com>
> ---
>   drivers/cpufreq/acpi-cpufreq.c | 5 ++++-
>   1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
> index 87e4923..cd2ca87 100644
> --- a/drivers/cpufreq/acpi-cpufreq.c
> +++ b/drivers/cpufreq/acpi-cpufreq.c
> @@ -517,11 +517,14 @@ static void free_acpi_perf_data(void)
>   {
>   	unsigned int i;
>   
> -	/* Freeing a NULL pointer is OK, and alloc_percpu zeroes. */
> +	if (!acpi_perf_data)
> +		return;
> +
>   	for_each_possible_cpu(i)
>   		free_cpumask_var(per_cpu_ptr(acpi_perf_data, i)
>   				 ->shared_cpu_map);
>   	free_percpu(acpi_perf_data);
> +	acpi_perf_data = NULL;
>   }
>   
>   static int cpufreq_boost_down_prep(unsigned int cpu)


-- 
Thx and BRs,
Zhongqiu Han

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] cpufreq: acpi-cpufreq: add NULL check for acpi_perf_data before freeing
  2026-08-10 11:21 ` [PATCH] cpufreq: acpi-cpufreq: add NULL check for acpi_perf_data before freeing Zhongqiu Han
@ 2026-08-10 11:52   ` Zhongqiu Han
  2026-08-12 12:06   ` 答复: [外部邮件] " Li,Rongqing
  1 sibling, 0 replies; 8+ messages in thread
From: Zhongqiu Han @ 2026-08-10 11:52 UTC (permalink / raw)
  To: lirongqing, Rafael J . Wysocki, Viresh Kumar, linux-pm; +Cc: zhongqiu.han

On 8/10/2026 7:21 PM, Zhongqiu Han wrote:
> On 8/10/2026 2:10 PM, lirongqing wrote:
>> From: Li RongQing <lirongqing@baidu.com>
>>
>> If alloc_percpu() fails in acpi_cpufreq_early_init(), acpi_perf_data is
>> NULL.  Calling free_acpi_perf_data() will execute per_cpu_ptr(NULL, i),
>> which does not return NULL but an offset pointer.
> 
> Hi RongQing,
> 
> Could you please confirm if acpi_perf_data() can ever be executed when 
> alloc_percpu() fails?

Sorry, typo: acpi_perf_data() --> free_acpi_perf_data()

> 
> 
>> Dereferencing ->shared_cpu_map on this invalid pointer leads to a kernel
>> crash.
>>
>> Fix these issues by adding a NULL check at the start of
>> free_acpi_perf_data() and setting acpi_perf_data to NULL after freeing.
>>
>> Signed-off-by: Li RongQing <lirongqing@baidu.com>
>> ---
>>   drivers/cpufreq/acpi-cpufreq.c | 5 ++++-
>>   1 file changed, 4 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi- 
>> cpufreq.c
>> index 87e4923..cd2ca87 100644
>> --- a/drivers/cpufreq/acpi-cpufreq.c
>> +++ b/drivers/cpufreq/acpi-cpufreq.c
>> @@ -517,11 +517,14 @@ static void free_acpi_perf_data(void)
>>   {
>>       unsigned int i;
>> -    /* Freeing a NULL pointer is OK, and alloc_percpu zeroes. */
>> +    if (!acpi_perf_data)
>> +        return;
>> +
>>       for_each_possible_cpu(i)
>>           free_cpumask_var(per_cpu_ptr(acpi_perf_data, i)
>>                    ->shared_cpu_map);
>>       free_percpu(acpi_perf_data);
>> +    acpi_perf_data = NULL;
>>   }
>>   static int cpufreq_boost_down_prep(unsigned int cpu)
> 
> 


-- 
Thx and BRs,
Zhongqiu Han

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] cpufreq: acpi-cpufreq: Initialize cmd.val in drv_read()
  2026-08-10  6:10 ` [PATCH] cpufreq: acpi-cpufreq: Initialize cmd.val in drv_read() lirongqing
@ 2026-08-10 12:15   ` Zhongqiu Han
  0 siblings, 0 replies; 8+ messages in thread
From: Zhongqiu Han @ 2026-08-10 12:15 UTC (permalink / raw)
  To: lirongqing, Rafael J . Wysocki, Viresh Kumar, linux-pm; +Cc: zhongqiu.han

On 8/10/2026 2:10 PM, lirongqing wrote:
> From: Li RongQing <lirongqing@baidu.com>
> 
> In drv_read(), struct drv_cmd cmd is allocated on the stack with .val left
> uninitialized. If smp_call_function_any() fails, do_drv_read() will not
> run to populate cmd.val. As a result, drv_read() would return uninitialized
> stack data.
> 
> Fix this by explicitly initializing .val to 0 when declaring cmd.
> 
> Signed-off-by: Li RongQing <lirongqing@baidu.com>
> ---
>   drivers/cpufreq/acpi-cpufreq.c | 1 +
>   1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
> index cd2ca87..6c7ece9 100644
> --- a/drivers/cpufreq/acpi-cpufreq.c
> +++ b/drivers/cpufreq/acpi-cpufreq.c
> @@ -308,6 +308,7 @@ static u32 drv_read(struct acpi_cpufreq_data *data, const struct cpumask *mask)
>   	struct acpi_processor_performance *perf = to_perf_data(data);
>   	struct drv_cmd cmd = {
>   		.reg = &perf->control_register,
> +		.val = 0,

Will the compiler handle this automatically?

https://en.cppreference.com/c/language/initialization


>   		.func.read = data->cpu_freq_read,
>   	};
>   	int err;


-- 
Thx and BRs,
Zhongqiu Han

^ permalink raw reply	[flat|nested] 8+ messages in thread

* 答复: [外部邮件] Re: [PATCH] cpufreq: acpi-cpufreq: add NULL check for acpi_perf_data before freeing
  2026-08-10 11:21 ` [PATCH] cpufreq: acpi-cpufreq: add NULL check for acpi_perf_data before freeing Zhongqiu Han
  2026-08-10 11:52   ` Zhongqiu Han
@ 2026-08-12 12:06   ` Li,Rongqing
  1 sibling, 0 replies; 8+ messages in thread
From: Li,Rongqing @ 2026-08-12 12:06 UTC (permalink / raw)
  To: Zhongqiu Han, Rafael J . Wysocki, Viresh Kumar,
	linux-pm@vger.kernel.org



> >
> > If alloc_percpu() fails in acpi_cpufreq_early_init(), acpi_perf_data
> > is NULL.  Calling free_acpi_perf_data() will execute per_cpu_ptr(NULL,
> > i), which does not return NULL but an offset pointer.
> 
> Hi RongQing,
> 
> Could you please confirm if acpi_perf_data() can ever be executed when
> alloc_percpu() fails?

You are right, acpi_perf_data() can not be called when alloc_percpu() fails

Please drop this patch

thanks 

[Li,Rongqing] 



> 
> 
> > Dereferencing ->shared_cpu_map on this invalid pointer leads to a kernel
> > crash.
> >
> > Fix these issues by adding a NULL check at the start of
> > free_acpi_perf_data() and setting acpi_perf_data to NULL after freeing.
> >
> > Signed-off-by: Li RongQing <lirongqing@baidu.com>
> > ---
> >   drivers/cpufreq/acpi-cpufreq.c | 5 ++++-
> >   1 file changed, 4 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
> > index 87e4923..cd2ca87 100644
> > --- a/drivers/cpufreq/acpi-cpufreq.c
> > +++ b/drivers/cpufreq/acpi-cpufreq.c
> > @@ -517,11 +517,14 @@ static void free_acpi_perf_data(void)
> >   {
> >   	unsigned int i;
> >
> > -	/* Freeing a NULL pointer is OK, and alloc_percpu zeroes. */
> > +	if (!acpi_perf_data)
> > +		return;
> > +
> >   	for_each_possible_cpu(i)
> >   		free_cpumask_var(per_cpu_ptr(acpi_perf_data, i)
> >   				 ->shared_cpu_map);
> >   	free_percpu(acpi_perf_data);
> > +	acpi_perf_data = NULL;
> >   }
> >
> >   static int cpufreq_boost_down_prep(unsigned int cpu)
> 
> 
> --
> Thx and BRs,
> Zhongqiu Han

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] cpufreq: acpi-cpufreq: Using cpufreq_for_each_entry() to iterate in extract_io()
  2026-08-10  6:10 ` [PATCH] cpufreq: acpi-cpufreq: Using cpufreq_for_each_entry() to iterate in extract_io() lirongqing
@ 2026-08-12 13:59   ` Zhongqiu Han
  0 siblings, 0 replies; 8+ messages in thread
From: Zhongqiu Han @ 2026-08-12 13:59 UTC (permalink / raw)
  To: lirongqing, Rafael J . Wysocki, Viresh Kumar, linux-pm
  Cc: zhongqiu.han, linux-kernel@vger.kernel.org

On 8/10/2026 2:10 PM, lirongqing wrote:
> From: Li RongQing <lirongqing@baidu.com>
> 
> In extract_io(), the loop iterates up to perf->state_count. However,
> when building policy->freq_table in acpi_cpufreq_cpu_init(), duplicate
> frequency entries are skipped, making freq_table smaller than
> perf->state_count.
> 
> Iterating perf->state_count times directly over policy->freq_table[i] can
> result in out-of-bounds array reads. Furthermore, policy->freq_table[i]

There is no out-of-bounds access. The array has "state_count + 1"
elements, so every index in "[0, state_count)" is inside the allocation.
right? The changelog looks like a memory-safety fix, which it is not,
and that wording alone would get the patch (mis)routed to stable and to
CVE bots.

> does not necessarily correspond to perf->states[i], as the original P-state
> index is stored in freq_table[entry].driver_data.

Yes, the real defect is the index space mismatch.

Might be good to note the side effects of it, AFAICT

freq_table[i] is not perf->states[i] once any _PSS entry has been
skipped. The function can therefore return a frequency belonging to a
different P-state, or 0 (zeroed tail entries), or CPUFREQ_TABLE_END
(~1u) when "i == valid_states", i.e. 0xfffffffe kHz reported
as a frequency. That last one is worth spelling out.

> 
> Fix this by using cpufreq_for_each_entry() to iterate over
> policy->freq_table, similar to extract_msr().
> 

Please add one Fixes tag here as well.

> Signed-off-by: Li RongQing <lirongqing@baidu.com>
> ---
>   drivers/cpufreq/acpi-cpufreq.c | 9 ++++-----
>   1 file changed, 4 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
> index 21639d9..87e4923 100644
> --- a/drivers/cpufreq/acpi-cpufreq.c
> +++ b/drivers/cpufreq/acpi-cpufreq.c
> @@ -196,15 +196,14 @@ static int check_amd_hwpstate_cpu(unsigned int cpuid)
>   static unsigned extract_io(struct cpufreq_policy *policy, u32 value)
>   {
>   	struct acpi_cpufreq_data *data = policy->driver_data;
> +	struct cpufreq_frequency_table *pos;
>   	struct acpi_processor_performance *perf;
> -	int i;
>   
>   	perf = to_perf_data(data);
>   
> -	for (i = 0; i < perf->state_count; i++) {
> -		if (value == perf->states[i].status)
> -			return policy->freq_table[i].frequency;
> -	}
> +	cpufreq_for_each_entry(pos, policy->freq_table)
> +		if (value == perf->states[pos->driver_data].status)
> +			return pos->frequency;
>   	return 0;
>   }

One more potential same issue is in func get_cur_freq_on_cpu()

cached_freq = policy->freq_table[to_perf_data(data)->state].frequency;

It is better to fix it as well.

For v2, please use ./scripts/get_maintainer.pl to generate the CC list
so linux-kernel@vger.kernel.org doesn't get missed. Thanks


>   


-- 
Thx and BRs,
Zhongqiu Han

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-08-12 13:59 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-10  6:10 [PATCH] cpufreq: acpi-cpufreq: add NULL check for acpi_perf_data before freeing lirongqing
2026-08-10  6:10 ` [PATCH] cpufreq: acpi-cpufreq: Initialize cmd.val in drv_read() lirongqing
2026-08-10 12:15   ` Zhongqiu Han
2026-08-10  6:10 ` [PATCH] cpufreq: acpi-cpufreq: Using cpufreq_for_each_entry() to iterate in extract_io() lirongqing
2026-08-12 13:59   ` Zhongqiu Han
2026-08-10 11:21 ` [PATCH] cpufreq: acpi-cpufreq: add NULL check for acpi_perf_data before freeing Zhongqiu Han
2026-08-10 11:52   ` Zhongqiu Han
2026-08-12 12:06   ` 答复: [外部邮件] " Li,Rongqing

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).