diff mbox series

cpufreq: brcmstb-avs-cpufreq: avoid a stuck risk and UAF issue in brcm_avs_cpufreq_get()

Message ID 1577513730-14254-1-git-send-email-qiwuchen55@gmail.com (mailing list archive)
State Not Applicable, archived
Headers show
Series cpufreq: brcmstb-avs-cpufreq: avoid a stuck risk and UAF issue in brcm_avs_cpufreq_get() | expand

Commit Message

chenqiwu Dec. 28, 2019, 6:15 a.m. UTC
From: chenqiwu <chenqiwu@xiaomi.com>

brcm_avs_cpufreq_get() calls cpufreq_cpu_get() to get cpufreq policy,
meanwhile, it also increments the kobject reference count of policy to
mark it busy. However, a corresponding call to cpufreq_cpu_put() is
ignored to decrement the kobject reference count back, which may lead
to a potential stuck risk that cpuhp thread deadly wait for dropping
of refcount when cpufreq policy free.

The call trace of stuck risk could be:
cpufreq_online()	//cpufreq initialization failed, goto out_free_policy.
    ->cpufreq_policy_free()	//do cpufreq_policy free.
	->cpufreq_policy_put_kobj()
            ->kobject_put()	//skip if policy kfref count is not 1.
                ->cpufreq_sysfs_release()
                    ->complete()	//complete policy->kobj_unregister.
                ->wait_for_completion() //wait for policy->kobj_unregister.

A simple way to avoid this stuck risk is use cpufreq_cpu_get_raw() instead
of cpufreq_cpu_get(), since brcmstb-avs driver just want to get cpufreq
policy in cpufreq_notify_transition().

What's more, there is a potential UAF issue in cpufreq_notify_transition()
that the cpufreq policy of current cpu has been released before using it.
So we should make a judgement to avoid it.

Thanks!
Qiwu

Signed-off-by: chenqiwu <chenqiwu@xiaomi.com>
---
 drivers/cpufreq/brcmstb-avs-cpufreq.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

Comments

chenqiwu Dec. 28, 2019, 12:47 p.m. UTC | #1
On Sat, Dec 28, 2019 at 06:20:25PM +0800, kbuild test robot wrote:
> Hi,
> 
> Thank you for the patch! Yet something to improve:
> 
> [auto build test ERROR on pm/linux-next]
> [also build test ERROR on v5.5-rc3 next-20191220]
> [if your patch is applied to the wrong git tree, please drop us a note to help
> improve the system. BTW, we also suggest to use '--base' option to specify the
> base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
> 
> url:    https://github.com/0day-ci/linux/commits/qiwuchen55-gmail-com/cpufreq-brcmstb-avs-cpufreq-avoid-a-stuck-risk-and-UAF-issue-in-brcm_avs_cpufreq_get/20191228-141943
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
> config: arm-allmodconfig (attached as .config)
> compiler: arm-linux-gnueabi-gcc (GCC) 7.5.0
> reproduce:
>         wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
>         chmod +x ~/bin/make.cross
>         # save the attached .config to linux build tree
>         GCC_VERSION=7.5.0 make.cross ARCH=arm 
> 
> If you fix the issue, kindly add following tag
> Reported-by: kbuild test robot <lkp@intel.com>
> 
> All error/warnings (new ones prefixed by >>):
> 
>    In file included from include/linux/pm_qos.h:10:0,
>                     from include/linux/cpufreq.h:16,
>                     from drivers/cpufreq/brcmstb-avs-cpufreq.c:44:
>    drivers/cpufreq/brcmstb-avs-cpufreq.c: In function 'brcm_avs_cpufreq_get':
> >> drivers/cpufreq/brcmstb-avs-cpufreq.c:459:12: error: 'dev' undeclared (first use in this function); did you mean 'sev'?
>       dev_warn(dev, "cpu %d: CPUFreq policy not found\n", cpu);
>                ^
>    include/linux/device.h:1776:12: note: in definition of macro 'dev_warn'
>      _dev_warn(dev, dev_fmt(fmt), ##__VA_ARGS__)
>                ^~~
>    drivers/cpufreq/brcmstb-avs-cpufreq.c:459:12: note: each undeclared identifier is reported only once for each function it appears in
>       dev_warn(dev, "cpu %d: CPUFreq policy not found\n", cpu);
>                ^
>    include/linux/device.h:1776:12: note: in definition of macro 'dev_warn'
>      _dev_warn(dev, dev_fmt(fmt), ##__VA_ARGS__)
>                ^~~
>    In file included from include/uapi/linux/posix_types.h:5:0,
>                     from include/uapi/linux/types.h:14,
>                     from include/linux/compiler.h:180,
>                     from include/linux/err.h:5,
>                     from include/linux/clk.h:12,
>                     from include/linux/cpufreq.h:11,
>                     from drivers/cpufreq/brcmstb-avs-cpufreq.c:44:
> >> include/linux/stddef.h:8:14: warning: return makes integer from pointer without a cast [-Wint-conversion]
>     #define NULL ((void *)0)
>                  ^
> >> drivers/cpufreq/brcmstb-avs-cpufreq.c:460:10: note: in expansion of macro 'NULL'
>       return NULL;
>              ^~~~
> >> include/linux/stddef.h:8:14: warning: return makes integer from pointer without a cast [-Wint-conversion]
>     #define NULL ((void *)0)
>                  ^
>    drivers/cpufreq/brcmstb-avs-cpufreq.c:465:10: note: in expansion of macro 'NULL'
>       return NULL;
>              ^~~~
> --
>    In file included from include/linux/pm_qos.h:10:0,
>                     from include/linux/cpufreq.h:16,
>                     from drivers//cpufreq/brcmstb-avs-cpufreq.c:44:
>    drivers//cpufreq/brcmstb-avs-cpufreq.c: In function 'brcm_avs_cpufreq_get':
>    drivers//cpufreq/brcmstb-avs-cpufreq.c:459:12: error: 'dev' undeclared (first use in this function); did you mean 'sev'?
>       dev_warn(dev, "cpu %d: CPUFreq policy not found\n", cpu);
>                ^
>    include/linux/device.h:1776:12: note: in definition of macro 'dev_warn'
>      _dev_warn(dev, dev_fmt(fmt), ##__VA_ARGS__)
>                ^~~
>    drivers//cpufreq/brcmstb-avs-cpufreq.c:459:12: note: each undeclared identifier is reported only once for each function it appears in
>       dev_warn(dev, "cpu %d: CPUFreq policy not found\n", cpu);
>                ^
>    include/linux/device.h:1776:12: note: in definition of macro 'dev_warn'
>      _dev_warn(dev, dev_fmt(fmt), ##__VA_ARGS__)
>                ^~~
>    In file included from include/uapi/linux/posix_types.h:5:0,
>                     from include/uapi/linux/types.h:14,
>                     from include/linux/compiler.h:180,
>                     from include/linux/err.h:5,
>                     from include/linux/clk.h:12,
>                     from include/linux/cpufreq.h:11,
>                     from drivers//cpufreq/brcmstb-avs-cpufreq.c:44:
> >> include/linux/stddef.h:8:14: warning: return makes integer from pointer without a cast [-Wint-conversion]
>     #define NULL ((void *)0)
>                  ^
>    drivers//cpufreq/brcmstb-avs-cpufreq.c:460:10: note: in expansion of macro 'NULL'
>       return NULL;
>              ^~~~
> >> include/linux/stddef.h:8:14: warning: return makes integer from pointer without a cast [-Wint-conversion]
>     #define NULL ((void *)0)
>                  ^
>    drivers//cpufreq/brcmstb-avs-cpufreq.c:465:10: note: in expansion of macro 'NULL'
>       return NULL;
>              ^~~~
> 
> vim +459 drivers/cpufreq/brcmstb-avs-cpufreq.c
> 
>    452	
>    453	static unsigned int brcm_avs_cpufreq_get(unsigned int cpu)
>    454	{
>    455		struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu);
>    456		struct private_data *priv;
>    457	
>    458		if (!policy) {
>  > 459			dev_warn(dev, "cpu %d: CPUFreq policy not found\n", cpu);
>  > 460			return NULL;
>    461		}
>    462	
>    463		priv = policy->driver_data;
>    464		if (!priv || !priv->base)
>    465			return NULL;
>    466	
>    467		return brcm_avs_get_frequency(priv->base);
>    468	}
>    469	
> 
> ---
> 0-DAY kernel test infrastructure                 Open Source Technology Center
> https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org Intel Corporation

So sorry, I sent the wrong patch file, and resend correct patch which compile ok.

Thanks!
Qiwu
diff mbox series

Patch

diff --git a/drivers/cpufreq/brcmstb-avs-cpufreq.c b/drivers/cpufreq/brcmstb-avs-cpufreq.c
index 77b0e5d..31aa76f 100644
--- a/drivers/cpufreq/brcmstb-avs-cpufreq.c
+++ b/drivers/cpufreq/brcmstb-avs-cpufreq.c
@@ -452,8 +452,17 @@  static bool brcm_avs_is_firmware_loaded(struct private_data *priv)
 
 static unsigned int brcm_avs_cpufreq_get(unsigned int cpu)
 {
-	struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
-	struct private_data *priv = policy->driver_data;
+	struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu);
+	struct private_data *priv;
+
+	if (!policy) {
+		dev_warn(dev, "cpu %d: CPUFreq policy not found\n", cpu);
+		return NULL;
+	}
+
+	priv = policy->driver_data;
+	if (!priv || !priv->base)
+		return NULL;
 
 	return brcm_avs_get_frequency(priv->base);
 }