Messages in this thread | | | Date | Fri, 13 May 2022 09:48:33 +0530 | From | Viresh Kumar <> | Subject | Re: [PATCH v4 1/2] cpufreq: fix race on cpufreq online |
| |
On 12-05-22, 21:52, Schspa Shi wrote: > When cpufreq online failed, policy->cpus are not empty while > cpufreq sysfs file available, we may access some data freed. > > Take policy->clk as an example: > > static int cpufreq_online(unsigned int cpu) > { > ... > // policy->cpus != 0 at this time > down_write(&policy->rwsem); > ret = cpufreq_add_dev_interface(policy); > up_write(&policy->rwsem); > > down_write(&policy->rwsem); > ... > /* cpufreq nitialization fails in some cases */ > if (cpufreq_driver->get && has_target()) { > policy->cur = cpufreq_driver->get(policy->cpu); > if (!policy->cur) { > ret = -EIO; > pr_err("%s: ->get() failed\n", __func__); > goto out_destroy_policy; > } > } > ... > up_write(&policy->rwsem); > ... > > return 0; > > out_destroy_policy: > for_each_cpu(j, policy->real_cpus) > remove_cpu_dev_symlink(policy, get_cpu_device(j)); > up_write(&policy->rwsem); > ... > out_exit_policy: > if (cpufreq_driver->exit) > cpufreq_driver->exit(policy); > clk_put(policy->clk); > // policy->clk is a wild pointer > ... > ^ > | > Another process access > __cpufreq_get > cpufreq_verify_current_freq > cpufreq_generic_get > // acces wild pointer of policy->clk; > | > | > out_offline_policy: | > cpufreq_policy_free(policy); | > // deleted here, and will wait for no body reference > cpufreq_policy_put_kobj(policy); > } > We can fix it by clear the policy->cpus mask. > Both show_scaling_cur_freq and show_cpuinfo_cur_freq will return an > error by checking this mask, thus avoiding UAF.
Instead of all above, what about this.
Subject: Abort show/store for half initialized policy
If policy initialization fails after the sysfs files are created, there is a possibility that we may end up running show()/store() callbacks for half initialized policies, which may have unpredictable outcomes.
Abort show/store in such a case by making sure the policy is active. Also inactivate the policy on such failures.
> Signed-off-by: Schspa Shi <schspa@gmail.com> > > --- > > Changelog: > v1 -> v2: > - Fix bad critical region enlarge which causes uninitialized > unlock. > - Move cpumask_clear(policy->cpus); before out_offline_policy > v2 -> v3: > - Remove the missed down_write() before > cpumask_and(policy->cpus, policy->cpus, cpu_online_mask); > v3 -> v4: > - Seprate to two patchs. > - Add policy_is_inactive check before sysfs access > --- > drivers/cpufreq/cpufreq.c | 11 +++++++++-- > 1 file changed, 9 insertions(+), 2 deletions(-) > > diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c > index 80f535cc8a75..35dffd738580 100644 > --- a/drivers/cpufreq/cpufreq.c > +++ b/drivers/cpufreq/cpufreq.c > @@ -953,7 +953,10 @@ static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf) > return -EIO; > > down_read(&policy->rwsem); > - ret = fattr->show(policy, buf); > + if (unlikely(policy_is_inactive(policy))) > + ret = -EBUSY; > + else > + ret = fattr->show(policy, buf);
I like it the way I have done earlier, initialize ret to -EBUSY and get rid of the else part and call show/store in if itself. Same for below.
> up_read(&policy->rwsem); > > return ret; > @@ -978,7 +981,10 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr, > > if (cpu_online(policy->cpu)) { > down_write(&policy->rwsem); > - ret = fattr->store(policy, buf, count); > + if (unlikely(policy_is_inactive(policy))) > + ret = -EBUSY; > + else > + ret = fattr->store(policy, buf, count); > up_write(&policy->rwsem); > } > > @@ -1533,6 +1539,7 @@ static int cpufreq_online(unsigned int cpu) > for_each_cpu(j, policy->real_cpus) > remove_cpu_dev_symlink(policy, get_cpu_device(j)); > > + cpumask_clear(policy->cpus); > up_write(&policy->rwsem); > > out_offline_policy: > -- > 2.29.0
-- viresh
| |