From dd4f730b557ce701a2cd4f604bf1e57667bd8b6e Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 10 Feb 2025 21:28:25 -0500 Subject: [PATCH 1/2] ACPI: platform-profile: Fix CFI violation when accessing sysfs files When an attribute group is created with sysfs_create_group(), the ->sysfs_ops() callback is set to kobj_sysfs_ops, which sets the ->show() and ->store() callbacks to kobj_attr_show() and kobj_attr_store() respectively. These functions use container_of() to get the respective callback from the passed attribute, meaning that these callbacks need to be of the same type as the callbacks in 'struct kobj_attribute'. However, ->show() and ->store() in the platform_profile driver are defined for struct device_attribute with the help of DEVICE_ATTR_RO() and DEVICE_ATTR_RW(), which results in a CFI violation when accessing platform_profile or platform_profile_choices under /sys/firmware/acpi because the types do not match: CFI failure at kobj_attr_show+0x19/0x30 (target: platform_profile_choices_show+0x0/0x140; expected type: 0x7a69590c) There is no functional issue from the type mismatch because the layout of 'struct kobj_attribute' and 'struct device_attribute' are the same, so the container_of() cast does not break anything aside from CFI. Change the type of platform_profile_choices_show() and platform_profile_{show,store}() to match the callbacks in 'struct kobj_attribute' and update the attribute variables to match, which resolves the CFI violation. Cc: All applicable Fixes: a2ff95e018f1 ("ACPI: platform: Add platform profile support") Reported-by: John Rowley Closes: https://github.com/ClangBuiltLinux/linux/issues/2047 Tested-by: John Rowley Reviewed-by: Sami Tolvanen Signed-off-by: Nathan Chancellor Acked-by: Greg Kroah-Hartman Reviewed-by: Mark Pearson Tested-by: Mark Pearson Link: https://patch.msgid.link/20250210-acpi-platform_profile-fix-cfi-violation-v3-1-ed9e9901c33a@kernel.org [ rjw: Changelog edits ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/platform_profile.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c index fc92e43d0fe9..1b6317f759f9 100644 --- a/drivers/acpi/platform_profile.c +++ b/drivers/acpi/platform_profile.c @@ -260,14 +260,14 @@ static int _aggregate_choices(struct device *dev, void *data) /** * platform_profile_choices_show - Show the available profile choices for legacy sysfs interface - * @dev: The device + * @kobj: The kobject * @attr: The attribute * @buf: The buffer to write to * * Return: The number of bytes written */ -static ssize_t platform_profile_choices_show(struct device *dev, - struct device_attribute *attr, +static ssize_t platform_profile_choices_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) { unsigned long aggregate[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)]; @@ -333,14 +333,14 @@ static int _store_and_notify(struct device *dev, void *data) /** * platform_profile_show - Show the current profile for legacy sysfs interface - * @dev: The device + * @kobj: The kobject * @attr: The attribute * @buf: The buffer to write to * * Return: The number of bytes written */ -static ssize_t platform_profile_show(struct device *dev, - struct device_attribute *attr, +static ssize_t platform_profile_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) { enum platform_profile_option profile = PLATFORM_PROFILE_LAST; @@ -362,15 +362,15 @@ static ssize_t platform_profile_show(struct device *dev, /** * platform_profile_store - Set the profile for legacy sysfs interface - * @dev: The device + * @kobj: The kobject * @attr: The attribute * @buf: The buffer to read from * @count: The number of bytes to read * * Return: The number of bytes read */ -static ssize_t platform_profile_store(struct device *dev, - struct device_attribute *attr, +static ssize_t platform_profile_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) { unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)]; @@ -401,12 +401,12 @@ static ssize_t platform_profile_store(struct device *dev, return count; } -static DEVICE_ATTR_RO(platform_profile_choices); -static DEVICE_ATTR_RW(platform_profile); +static struct kobj_attribute attr_platform_profile_choices = __ATTR_RO(platform_profile_choices); +static struct kobj_attribute attr_platform_profile = __ATTR_RW(platform_profile); static struct attribute *platform_profile_attrs[] = { - &dev_attr_platform_profile_choices.attr, - &dev_attr_platform_profile.attr, + &attr_platform_profile_choices.attr, + &attr_platform_profile.attr, NULL }; From bb519cf6113473c09d571e555137a36d7e2c8566 Mon Sep 17 00:00:00 2001 From: Kurt Borja Date: Wed, 12 Feb 2025 14:03:08 -0500 Subject: [PATCH 2/2] ACPI: platform_profile: Improve platform_profile_unregister() Drivers usually call this method on error/exit paths and do not check for it's return value, which is always 0 anyway, so make it void. This is safe to do as currently all drivers use devm_platform_profile_register(). While at it, improve the style and make the function safer by checking for IS_ERR_OR_NULL before dereferencing the device pointer. Signed-off-by: Kurt Borja Reviewed-by: Mark Pearson Link: https://patch.msgid.link/20250212190308.21209-1-kuurtb@gmail.com [ rjw: Minor changelog edits ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/platform_profile.c | 19 +++++++++---------- include/linux/platform_profile.h | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c index 1b6317f759f9..f01455905830 100644 --- a/drivers/acpi/platform_profile.c +++ b/drivers/acpi/platform_profile.c @@ -569,24 +569,23 @@ EXPORT_SYMBOL_GPL(platform_profile_register); /** * platform_profile_remove - Unregisters a platform profile class device * @dev: Class device - * - * Return: 0 */ -int platform_profile_remove(struct device *dev) +void platform_profile_remove(struct device *dev) { - struct platform_profile_handler *pprof = to_pprof_handler(dev); - int id; + struct platform_profile_handler *pprof; + + if (IS_ERR_OR_NULL(dev)) + return; + + pprof = to_pprof_handler(dev); + guard(mutex)(&profile_lock); - id = pprof->minor; + ida_free(&platform_profile_ida, pprof->minor); device_unregister(&pprof->dev); - ida_free(&platform_profile_ida, id); sysfs_notify(acpi_kobj, NULL, "platform_profile"); - sysfs_update_group(acpi_kobj, &platform_profile_group); - - return 0; } EXPORT_SYMBOL_GPL(platform_profile_remove); diff --git a/include/linux/platform_profile.h b/include/linux/platform_profile.h index 8ab5b0e8eb2c..d5499eca9e1d 100644 --- a/include/linux/platform_profile.h +++ b/include/linux/platform_profile.h @@ -47,7 +47,7 @@ struct platform_profile_ops { struct device *platform_profile_register(struct device *dev, const char *name, void *drvdata, const struct platform_profile_ops *ops); -int platform_profile_remove(struct device *dev); +void platform_profile_remove(struct device *dev); struct device *devm_platform_profile_register(struct device *dev, const char *name, void *drvdata, const struct platform_profile_ops *ops);