mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 16:29:59 +00:00
![James Morse](/assets/img/avatar_default.png)
Since commite5c6b312ce
("cpufreq: schedutil: Use kobject release() method to free sugov_tunables") kobject_put() has kfree()d the attr_set before gov_attr_set_put() returns. kobject_put() isn't the last user of attr_set in gov_attr_set_put(), the subsequent mutex_destroy() triggers a use-after-free: | BUG: KASAN: use-after-free in mutex_is_locked+0x20/0x60 | Read of size 8 at addr ffff000800ca4250 by task cpuhp/2/20 | | CPU: 2 PID: 20 Comm: cpuhp/2 Not tainted 5.15.0-rc1 #12369 | Hardware name: ARM LTD ARM Juno Development Platform/ARM Juno Development | Platform, BIOS EDK II Jul 30 2018 | Call trace: | dump_backtrace+0x0/0x380 | show_stack+0x1c/0x30 | dump_stack_lvl+0x8c/0xb8 | print_address_description.constprop.0+0x74/0x2b8 | kasan_report+0x1f4/0x210 | kasan_check_range+0xfc/0x1a4 | __kasan_check_read+0x38/0x60 | mutex_is_locked+0x20/0x60 | mutex_destroy+0x80/0x100 | gov_attr_set_put+0xfc/0x150 | sugov_exit+0x78/0x190 | cpufreq_offline.isra.0+0x2c0/0x660 | cpuhp_cpufreq_offline+0x14/0x24 | cpuhp_invoke_callback+0x430/0x6d0 | cpuhp_thread_fun+0x1b0/0x624 | smpboot_thread_fn+0x5e0/0xa6c | kthread+0x3a0/0x450 | ret_from_fork+0x10/0x20 Swap the order of the calls. Fixes:e5c6b312ce
("cpufreq: schedutil: Use kobject release() method to free sugov_tunables") Cc: 4.7+ <stable@vger.kernel.org> # 4.7+ Signed-off-by: James Morse <james.morse@arm.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
81 lines
2.1 KiB
C
81 lines
2.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Abstract code for CPUFreq governor tunable sysfs attributes.
|
|
*
|
|
* Copyright (C) 2016, Intel Corporation
|
|
* Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
*/
|
|
|
|
#include "cpufreq_governor.h"
|
|
|
|
static inline struct gov_attr_set *to_gov_attr_set(struct kobject *kobj)
|
|
{
|
|
return container_of(kobj, struct gov_attr_set, kobj);
|
|
}
|
|
|
|
static inline struct governor_attr *to_gov_attr(struct attribute *attr)
|
|
{
|
|
return container_of(attr, struct governor_attr, attr);
|
|
}
|
|
|
|
static ssize_t governor_show(struct kobject *kobj, struct attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct governor_attr *gattr = to_gov_attr(attr);
|
|
|
|
return gattr->show(to_gov_attr_set(kobj), buf);
|
|
}
|
|
|
|
static ssize_t governor_store(struct kobject *kobj, struct attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct gov_attr_set *attr_set = to_gov_attr_set(kobj);
|
|
struct governor_attr *gattr = to_gov_attr(attr);
|
|
int ret;
|
|
|
|
mutex_lock(&attr_set->update_lock);
|
|
ret = attr_set->usage_count ? gattr->store(attr_set, buf, count) : -EBUSY;
|
|
mutex_unlock(&attr_set->update_lock);
|
|
return ret;
|
|
}
|
|
|
|
const struct sysfs_ops governor_sysfs_ops = {
|
|
.show = governor_show,
|
|
.store = governor_store,
|
|
};
|
|
EXPORT_SYMBOL_GPL(governor_sysfs_ops);
|
|
|
|
void gov_attr_set_init(struct gov_attr_set *attr_set, struct list_head *list_node)
|
|
{
|
|
INIT_LIST_HEAD(&attr_set->policy_list);
|
|
mutex_init(&attr_set->update_lock);
|
|
attr_set->usage_count = 1;
|
|
list_add(list_node, &attr_set->policy_list);
|
|
}
|
|
EXPORT_SYMBOL_GPL(gov_attr_set_init);
|
|
|
|
void gov_attr_set_get(struct gov_attr_set *attr_set, struct list_head *list_node)
|
|
{
|
|
mutex_lock(&attr_set->update_lock);
|
|
attr_set->usage_count++;
|
|
list_add(list_node, &attr_set->policy_list);
|
|
mutex_unlock(&attr_set->update_lock);
|
|
}
|
|
EXPORT_SYMBOL_GPL(gov_attr_set_get);
|
|
|
|
unsigned int gov_attr_set_put(struct gov_attr_set *attr_set, struct list_head *list_node)
|
|
{
|
|
unsigned int count;
|
|
|
|
mutex_lock(&attr_set->update_lock);
|
|
list_del(list_node);
|
|
count = --attr_set->usage_count;
|
|
mutex_unlock(&attr_set->update_lock);
|
|
if (count)
|
|
return count;
|
|
|
|
mutex_destroy(&attr_set->update_lock);
|
|
kobject_put(&attr_set->kobj);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(gov_attr_set_put);
|