cpc_desc_ptr embeds a struct kobject that is registered in sysfs via
kobject_init_and_add(), but cppc_ktype lacked a .release callback and
acpi_cppc_processor_exit() freed cpc_ptr immediately after kobject_put().
With CONFIG_DEBUG_KOBJECT_RELEASE=y, this causes a use-after-free when
kobject_delayed_cleanup() runs on freed memory.
Move the sys_mem_vaddr unmapping and kfree(cpc_ptr) into a new
cppc_release() callback on cppc_ktype, and initialize the kobject on
allocation so kobject_put() handles cleanup in all paths.
Fixes: 158c998ea44b ("ACPI / CPPC: add sysfs support to compute delivered
performance")
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Ian Rogers <[email protected]>
---
drivers/acpi/cppc_acpi.c | 39 ++++++++++++++++++---------------------
1 file changed, 18 insertions(+), 21 deletions(-)
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index fef54fcd00b7..2a80404f85d3 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -297,7 +297,22 @@ static struct attribute *cppc_attrs[] = {
};
ATTRIBUTE_GROUPS(cppc);
+static void cppc_release(struct kobject *kobj)
+{
+ struct cpc_desc *cpc_ptr = to_cpc_desc(kobj);
+ unsigned int i;
+
+ for (i = 2; i < cpc_ptr->num_entries; i++) {
+ void __iomem *addr = cpc_ptr->cpc_regs[i - 2].sys_mem_vaddr;
+
+ if (addr)
+ iounmap(addr);
+ }
+ kfree(cpc_ptr);
+}
+
static const struct kobj_type cppc_ktype = {
+ .release = cppc_release,
.sysfs_ops = &kobj_sysfs_ops,
.default_groups = cppc_groups,
};
@@ -799,6 +814,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
ret = -ENOMEM;
goto out_buf_free;
}
+ kobject_init(&cpc_ptr->kobj, &cppc_ktype);
/* First entry is NumEntries. */
cpc_obj = &out_obj->package.elements[0];
@@ -1001,11 +1017,9 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
/* Plug PSD data into this CPU's CPC descriptor. */
per_cpu(cpc_desc_ptr, pr->id) = cpc_ptr;
- ret = kobject_init_and_add(&cpc_ptr->kobj, &cppc_ktype, &cpu_dev->kobj,
- "acpi_cppc");
+ ret = kobject_add(&cpc_ptr->kobj, &cpu_dev->kobj, "acpi_cppc");
if (ret) {
per_cpu(cpc_desc_ptr, pr->id) = NULL;
- kobject_put(&cpc_ptr->kobj);
goto out_free;
}
@@ -1013,14 +1027,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
return 0;
out_free:
- /* Free all the mapped sys mem areas for this CPU */
- for (i = 2; i < cpc_ptr->num_entries; i++) {
- void __iomem *addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr;
-
- if (addr)
- iounmap(addr);
- }
- kfree(cpc_ptr);
+ kobject_put(&cpc_ptr->kobj);
out_buf_free:
kfree(output.pointer);
@@ -1037,8 +1044,6 @@ EXPORT_SYMBOL_GPL(acpi_cppc_processor_probe);
void acpi_cppc_processor_exit(struct acpi_processor *pr)
{
struct cpc_desc *cpc_ptr;
- unsigned int i;
- void __iomem *addr;
int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, pr->id);
if (pcc_ss_id >= 0 && pcc_data[pcc_ss_id]) {
@@ -1056,15 +1061,7 @@ void acpi_cppc_processor_exit(struct acpi_processor *pr)
if (!cpc_ptr)
return;
- /* Free all the mapped sys mem areas for this CPU */
- for (i = 2; i < cpc_ptr->num_entries; i++) {
- addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr;
- if (addr)
- iounmap(addr);
- }
-
kobject_put(&cpc_ptr->kobj);
- kfree(cpc_ptr);
}
EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit);
--
2.56.0.385.gd3acb90ef8-goog