From: Schspa Shi <schspa@gmail.com>
To: "Rafael J. Wysocki" <rafael@kernel.org>
Cc: Viresh Kumar <viresh.kumar@linaro.org>,
Linux Kernel Mailing List <linux-kernel@vger.kernel.org>,
Linux PM <linux-pm@vger.kernel.org>
Subject: Re: [PATCH v3] cpufreq: fix race on cpufreq online
Date: Thu, 12 May 2022 13:51:53 +0800 [thread overview]
Message-ID: <m2wner47ru.fsf@gmail.com> (raw)
In-Reply-To: <CAJZ5v0gcOmd8fXG9_BYxr6rN7ncUWnfki7K9S5wK2Vvh9SxUCA@mail.gmail.com>
"Rafael J. Wysocki" <rafael@kernel.org> writes:
> On Tue, May 10, 2022 at 5:42 PM Schspa Shi <schspa@gmail.com>
> 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.
>
> So the UAF only happens if something is freed by ->offline() or
> ->exit() and I'm not sure where the mask is checked in the
> scaling_cur_freq() path.
>
In the current code, it is checked in the following path:
show();
down_read(&policy->rwsem);
ret = fattr->show(policy, buf);
show_cpuinfo_cur_freq
__cpufreq_get
if (unlikely(policy_is_inactive(policy)))
return 0;
up_read(&policy->rwsem);
> Overall, the patch is really two changes in one IMO.
>
>> Signed-off-by: Schspa Shi <schspa@gmail.com>
>>
>> ---
>>
>> Changelog:
>> v1 -> v2:
>> - Fix bad critical region enlarge which causes
>> uninitialized
>> unlock.
>> v2 -> v3:
>> - Remove the missed down_write() before
>> cpumask_and(policy->cpus, policy->cpus,
>> cpu_online_mask);
>>
>> Signed-off-by: Schspa Shi <schspa@gmail.com>
>> ---
>> drivers/cpufreq/cpufreq.c | 6 +++---
>> 1 file changed, 3 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/cpufreq/cpufreq.c
>> b/drivers/cpufreq/cpufreq.c
>> index 80f535cc8a75..d93958dbdab8 100644
>> --- a/drivers/cpufreq/cpufreq.c
>> +++ b/drivers/cpufreq/cpufreq.c
>> @@ -1337,12 +1337,12 @@ static int cpufreq_online(unsigned int
>> cpu)
>> down_write(&policy->rwsem);
>> policy->cpu = cpu;
>> policy->governor = NULL;
>> - up_write(&policy->rwsem);
>> } else {
>> new_policy = true;
>> policy = cpufreq_policy_alloc(cpu);
>> if (!policy)
>> return -ENOMEM;
>> + down_write(&policy->rwsem);
>> }
>>
>> if (!new_policy && cpufreq_driver->online) {
>> @@ -1382,7 +1382,6 @@ static int cpufreq_online(unsigned int
>> cpu)
>> cpumask_copy(policy->related_cpus,
>> policy->cpus);
>> }
>>
>> - down_write(&policy->rwsem);
>> /*
>> * affected cpus must always be the one, which are
>> online. We aren't
>> * managing offline cpus here.
>
> The first change, which could and probably should be a separate
> patch,
> ends here.
>
> You prevent the rwsem from being dropped in the existing policy
> case
> and acquire it right after creating a new policy.
>
> This way ->online() always runs under the rwsem, which
> definitely
> sounds like a good idea, and policy->cpus is manipulated under
> the
> rwsem which IMV is required.
>
> As a side-effect, ->init() is also run under the rwsem, but that
> shouldn't be a problem as per your discussion with Viresh.
>
> So the above would be patch 1 in a series.
>
> The change below is a separate one and it addresses the
> particular
> race you've discovered, as long as patch 1 above is present. It
> would
> be patch 2 in the series.
>
>> @@ -1533,7 +1532,7 @@ static int cpufreq_online(unsigned int
>> cpu)
>> for_each_cpu(j, policy->real_cpus)
>> remove_cpu_dev_symlink(policy,
>> get_cpu_device(j));
>>
>> - up_write(&policy->rwsem);
>> + cpumask_clear(policy->cpus);
>
> It is OK to clear policy->cpus here, because ->offline() and
> ->exit()
> are called with policy->cpus clear from cpufreq_offline() and
> cpufreq_remove_dev(), so they cannot assume policy->cpus to be
> populated when they are invoked. However, this needs to be
> stated in
> the changelog of patch 2.
>
OK, I will separate it into two patch.
>> out_offline_policy:
>> if (cpufreq_driver->offline)
>> @@ -1542,6 +1541,7 @@ static int cpufreq_online(unsigned int
>> cpu)
>> out_exit_policy:
>> if (cpufreq_driver->exit)
>> cpufreq_driver->exit(policy);
>> + up_write(&policy->rwsem);
>
> It is consistent to run ->offline() and ->exit() under the
> rwsem, so
> this change is OK too.
>
>> out_free_policy:
>> cpufreq_policy_free(policy);
>> --
>
> That said, there still are races that are not addressed by the
> above,
> so I would add patch 3 changing show() to check
> policy_is_inactive()
> under the rwsem.
>
Yes, let me upload a new patch for this change.
> Thanks!
---
BRs
Schspa Shi
next prev parent reply other threads:[~2022-05-12 5:52 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-04-20 19:15 [PATCH] cpufreq: fix race on cpufreq online Schspa Shi
2022-04-22 14:38 ` Rafael J. Wysocki
2022-04-22 15:10 ` Schspa Shi
2022-04-22 15:59 ` Rafael J. Wysocki
2022-05-09 3:57 ` Viresh Kumar
2022-05-09 15:06 ` Schspa Shi
2022-05-10 3:52 ` Viresh Kumar
2022-05-10 15:28 ` [PATCH v2] " Schspa Shi
2022-05-10 15:34 ` Rafael J. Wysocki
2022-05-10 15:43 ` Schspa Shi
2022-05-10 15:42 ` [PATCH v3] " Schspa Shi
2022-05-11 4:35 ` Viresh Kumar
2022-05-11 8:10 ` Schspa Shi
2022-05-11 12:21 ` Viresh Kumar
2022-05-11 12:59 ` Rafael J. Wysocki
2022-05-11 13:19 ` Rafael J. Wysocki
2022-05-11 13:42 ` Rafael J. Wysocki
2022-05-11 13:42 ` Schspa Shi
2022-05-11 13:50 ` Rafael J. Wysocki
2022-05-12 6:56 ` Viresh Kumar
2022-05-12 10:49 ` Rafael J. Wysocki
2022-05-13 4:27 ` Viresh Kumar
2022-05-24 11:14 ` Viresh Kumar
2022-05-24 11:22 ` Rafael J. Wysocki
2022-05-24 11:29 ` Viresh Kumar
2022-05-24 11:48 ` Rafael J. Wysocki
2022-05-24 11:53 ` Rafael J. Wysocki
2022-05-25 5:32 ` Viresh Kumar
2022-05-12 5:56 ` Viresh Kumar
2022-05-11 13:12 ` Schspa Shi
2022-05-11 14:15 ` Rafael J. Wysocki
2022-05-12 5:51 ` Schspa Shi [this message]
2022-05-12 10:37 ` Rafael J. Wysocki
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=m2wner47ru.fsf@gmail.com \
--to=schspa@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=rafael@kernel.org \
--cc=viresh.kumar@linaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.