drivers/cpufreq/cpufreq_ext.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-)
Switching to the 'ext' governor can trigger a NULL pointer dereference
in ext_gov_update().
Two problems were present in this function:
1) policy_dbs was derived via container_of(policy, ...), which is not
a valid way to obtain CPUFreq dbs-governor per-policy state. Use
policy->governor_data instead.
2) When the BPF hook returns 0 for get_sampling_rate, the code fell
back to gov->gdbs_data->sampling_rate, which may be NULL or stale
depending on initialization/teardown ordering. The robust pattern
is to fetch the per-policy sampling rate from policy_dbs->dbs_data.
This patch switches ext_gov_update() to the per-policy path
(policy->governor_data -> policy_dbs->dbs_data) and adds minimal NULL
checks. After this change, switching to the 'ext' governor no longer
panics in testing.
Observed on: Pixel 6 (oriole, arm64), Android kernel
6.12.0-mainline-g0a53f54ba5c5 (Jun 11, 2025), after applying the
cpufreq_ext RFC series. Also revalidated on a PC build.
Reproducer:
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
echo ext > "$cpu"
done
Link: https://lore.kernel.org/linux-pm/20240927101342.3240263-1-zouyipeng@huawei.com/
Signed-off-by: Jinghao Zhou <zhoujinghao24b@gmail.com>
---
drivers/cpufreq/cpufreq_ext.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/drivers/cpufreq/cpufreq_ext.c b/drivers/cpufreq/cpufreq_ext.c
index 310f13aca70a..c79068e86c27 100644
--- a/drivers/cpufreq/cpufreq_ext.c
+++ b/drivers/cpufreq/cpufreq_ext.c
@@ -409,11 +409,18 @@ static unsigned int ext_gov_update(struct cpufreq_policy *policy)
{
struct ext_policy *ext;
struct policy_dbs_info *policy_dbs;
+ struct dbs_data *dbs_data;
unsigned int update_sampling_rate = 0;
- struct dbs_governor *gov = dbs_governor_of(policy);
- /* Only need to update current policy freq */
- policy_dbs = container_of((void *)policy, struct policy_dbs_info, policy);
+ /* Get policy_dbs_info from policy's governor_data */
+ policy_dbs = policy->governor_data;
+ if (!policy_dbs)
+ return 0;
+
+ /* Get dbs_data directly from policy_dbs for better stability */
+ dbs_data = policy_dbs->dbs_data;
+ if (!dbs_data)
+ return 0;
ext = to_ext_policy(policy_dbs);
@@ -431,7 +438,7 @@ static unsigned int ext_gov_update(struct cpufreq_policy *policy)
update_sampling_rate = ext_ops_global.get_sampling_rate(policy);
/* If get_sampling_rate return 0, means we don't modify sampling_rate any more. */
- return update_sampling_rate == 0 ? gov->gdbs_data->sampling_rate : update_sampling_rate;
+ return update_sampling_rate == 0 ? dbs_data->sampling_rate : update_sampling_rate;
}
static struct policy_dbs_info *ext_gov_alloc(void)
--
2.34.1
© 2016 - 2025 Red Hat, Inc.