Re: [PATCH v2] cpufreq: amd-pstate-ut: fix null pointer dereference

From: Qianheng Peng

Date: Thu Jul 16 2026 - 00:01:04 EST


Hello Prateek, Zhongqiu,
Thank you both for the awesome analyses.

On 7/15/2026 20:21:34, K Prateek Nayak wrote:
>Hello Zhongqiu, Qianheng,
>
>Thank you both for taking a look at this!
>
>On 7/15/2026 7:30 PM, Zhongqiu Han wrote:
>> Hi Qianheng,
>>
>> On 7/15/2026 5:35 PM, Qianheng Peng wrote:
>>> The crash issue may occur when insmod amd_pstate_ut module on intel platform.
>>>
>>>     amd_pstate_ut: 1    amd_pstate_ut_acpi_cpc_valid  success!
>>>     amd_pstate_ut: 2    amd_pstate_ut_check_enabled   success!
>>>     BUG: kernel NULL pointer dereference, address: 0000000000000080
>>>     #PF: supervisor read access in kernel mode
>>>     #PF: error_code(0x0000) - not-present page
>>>     PGD 0 P4D 0
>>>     Oops: 0000 [#1] SMP NOPTI
>>>     CPU: 0 PID: 20300 Comm: modprobe
>>>     Kdump: loaded Tainted: G O 6.6.0-0010.rc1.ctl4.x86_64 #1
>>>     Hardware name: FiberHome R2200 V5/Xeon Boards, BIOS 3.1a 02/24/2020
>>>     RIP: 0010:amd_pstate_ut_check_perf+0x141/0x280 [amd_pstate_ut]
>>>     Call Trace:
>>>      <TASK>
>>>      amd_pstate_ut_init+0x1b/0xff0 [amd_pstate_ut]
>>>      ? __pfx_amd_pstate_ut_init+0x10/0x10 [amd_pstate_ut]
>>>      do_one_initcall+0x42/0x2e0
>>>      ? kmalloc_trace+0x26/0x90
>>>      do_init_module+0x60/0x240
>>>      __se_sys_init_module+0x185/0x1c0
>>>      do_syscall_64+0x62/0x190
>>>      entry_SYSCALL_64_after_hwframe+0x76/0x7e
>>>      </TASK>
>>>
>>> Add invalidation check of cpudata in amd_pstate_ut_check_perf() and
>>> amd_pstate_ut_check_freq() to avoid unpredicated null pointer dereference.
>>> Otherwise, add driver name detection for amd_pstate_ut driver to prevent it
>>
>> The driver-name check in amd_pstate_ut_init() is the actual fix: it
>> prevents this module from running its test cases on non-AMD platforms.
>>
>> The NULL check should not be necessary. On an already-registered amd
>> pstate driver, policy->driver_data is set only after a successful
>> ->init() and is cleared only in ->exit(), so it is never legitimately
>> NULL for a live policy:
>>
>> 1. Hotplug offline path: a normal CPU hotplug offline does not hit
>> ->exit(). The driver's .offline callback makes __cpufreq_offline() take
>> the light-weight teardown path and return early, so ->exit() (and the
>> kfree) is not invoked and cpudata stays valid. The full teardown only
>> happens on driver unregister / device unbind.
>>
>> 2. Driver exit path: the NULL check does not close the theoretical UAF
>> window here, since the test does not hold policy->rwsem: cpudata can be
>> kfree()'d between the check and the subsequent dereference. Worse, the
>> passive amd_pstate_cpu_exit() frees cpudata but does not reset policy
>> ->driver_data to NULL, so the check would pass and then dereference a
>> dangling pointer. This is a pre-existing issue and out of scope for this
>> fix.
>>
>> so it is better to drop the "NULL check of cpudata" commit msg and code.
>>
>> (Others may have more context here, so please free free correct me if
>> I'm wrong.)
>
>Ack! amd-pstate-ut was intended to be run in isolation without any
>other concurrent driver operation as a sanity check for developers.
>Races with hotplug / any user initiated changes during the test run
>can cause issues.
>
Yes, I agree that dropping NULL check of cpudata is reasonable
for the real user cases.
>>
>>
>>
>>> from testing cpufreq driver on non-amd platform.
>>>
>>> Fixes: 14eb1c96e3a3 ("cpufreq: amd-pstate: Add test module for amd-pstate driver")
>>> Suggested-by: Li Xiong <xiongl24@xxxxxxxxxxxxxxx>
>>> Suggested-by: Xibo Wang <wangxb12@xxxxxxxxxxxxxxx>
>>> Signed-off-by: Qianheng Peng <pengqh1@xxxxxxxxxxxxxxx>
>>>
>>> ---
>>> v2:
>>>    - Print policy->cpu instead of policy->kboj.name
>>>    - Add cpufreq driver name detection to amd_pstate_ut
>>>
>>> v1: https://lore.kernel.org/all/1784015941-26535-1-git-send-email-pengqh1@xxxxxxxxxxxxxxx
>>> ---
>>>   drivers/cpufreq/amd-pstate-ut.c | 17 +++++++++++++++++
>>>   1 file changed, 17 insertions(+)
>>>
>>> diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c
>>> index 735b29f..5d71315 100644
>>> --- a/drivers/cpufreq/amd-pstate-ut.c
>>> +++ b/drivers/cpufreq/amd-pstate-ut.c
>>> @@ -157,6 +157,10 @@ static int amd_pstate_ut_check_perf(u32 index)
>>>           if (!policy)
>>>               continue;
>>>           cpudata = policy->driver_data;
>>> +        if (!cpudata) {
>>> +            pr_err("%s empty driver_data on cpu %d\n", __func__, policy->cpu);
>>> +            return -EINVAL;
>>
>> Strictly speaking, it should be %u, right? Since policy->cpu is an
>> unsigned int, using %d is a format-specifier type mismatch.
>>
>>> +        }
>>>             if (get_shared_mem()) {
>>>               ret = cppc_get_perf_caps(cpu, &cppc_perf);
>>> @@ -229,6 +233,10 @@ static int amd_pstate_ut_check_freq(u32 index)
>>>           if (!policy)
>>>               continue;
>>>           cpudata = policy->driver_data;
>>> +        if (!cpudata) {
>>> +            pr_err("%s empty driver_data on cpu %d\n", __func__, policy->cpu);
>>> +            return -EINVAL;
>>> +        }
>>>             if (!((policy->cpuinfo.max_freq >= cpudata->nominal_freq) &&
>>>               (cpudata->nominal_freq > cpudata->lowest_nonlinear_freq) &&
>>> @@ -560,6 +568,15 @@ static int amd_pstate_ut_check_freq_attrs(u32 index)
>>>   static int __init amd_pstate_ut_init(void)
>>>   {
>>>       u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases);
>>> +    const char *driver_name = NULL;
>>> +
>>> +    driver_name = cpufreq_get_current_driver();
>>> +    if (!driver_name)
>>> +        return -ENOENT;
>>> +    /* don't test if non-amd cpufreq_driver exists */
>>> +    else if (strcmp(driver_name, "amd-pstate") &&
>>
>> Nit: Redundant else after a return: since the previous branch already
>> returns, control can never reach the else if, so it is better to drop
>> the else and use a separate if.
>
>A simpler check would be:
>
> enum amd_pstate_mode mode = amd_pstate_get_status();
>
> if (mode == AMD_PSTATE_UNDEFINED || mode == AMD_PSTATE_DISABLE)
> return -ENOENT;
>
>Status always starts out as undefined and if the driver doesn't
>load, it stays that way. If user has disabled the driver for any
>reason, the unit test should bail out too.
>
>>
>>> +         strcmp(driver_name, "amd-pstate-epp"))
>>> +        return -EEXIST;
>>
>> How about -EOPNOTSUPP or -ENODEV? And it would be helpful to log
>> error info here.
>>
>>
>>>         for (i = 0; i < arr_size; i++) {
>>>           int ret;
>>
>>
The simple check is honestly graceful, I will send it in patch v3.
And I also think -EOPNOTSUPP is better for telling current error.

--
Thanks and Regards,
Qianheng