From: Krzysztof Kozlowski krzysztof.kozlowski@linaro.org
commit 6c2f421174273de8f83cde4286d1c076d43a2d35 upstream.
Several core drivers and buses expect that driver_override is a dynamically allocated memory thus later they can kfree() it.
However such assumption is not documented, there were in the past and there are already users setting it to a string literal. This leads to kfree() of static memory during device release (e.g. in error paths or during unbind):
kernel BUG at ../mm/slub.c:3960! Internal error: Oops - BUG: 0 [#1] PREEMPT SMP ARM ... (kfree) from [<c058da50>] (platform_device_release+0x88/0xb4) (platform_device_release) from [<c0585be0>] (device_release+0x2c/0x90) (device_release) from [<c0a69050>] (kobject_put+0xec/0x20c) (kobject_put) from [<c0f2f120>] (exynos5_clk_probe+0x154/0x18c) (exynos5_clk_probe) from [<c058de70>] (platform_drv_probe+0x6c/0xa4) (platform_drv_probe) from [<c058b7ac>] (really_probe+0x280/0x414) (really_probe) from [<c058baf4>] (driver_probe_device+0x78/0x1c4) (driver_probe_device) from [<c0589854>] (bus_for_each_drv+0x74/0xb8) (bus_for_each_drv) from [<c058b48c>] (__device_attach+0xd4/0x16c) (__device_attach) from [<c058a638>] (bus_probe_device+0x88/0x90) (bus_probe_device) from [<c05871fc>] (device_add+0x3dc/0x62c) (device_add) from [<c075ff10>] (of_platform_device_create_pdata+0x94/0xbc) (of_platform_device_create_pdata) from [<c07600ec>] (of_platform_bus_create+0x1a8/0x4fc) (of_platform_bus_create) from [<c0760150>] (of_platform_bus_create+0x20c/0x4fc) (of_platform_bus_create) from [<c07605f0>] (of_platform_populate+0x84/0x118) (of_platform_populate) from [<c0f3c964>] (of_platform_default_populate_init+0xa0/0xb8) (of_platform_default_populate_init) from [<c01031f8>] (do_one_initcall+0x8c/0x404)
Provide a helper which clearly documents the usage of driver_override. This will allow later to reuse the helper and reduce the amount of duplicated code.
Convert the platform driver to use a new helper and make the driver_override field const char (it is not modified by the core).
Reviewed-by: Rafael J. Wysocki rafael.j.wysocki@intel.com Acked-by: Rafael J. Wysocki rafael.j.wysocki@intel.com Signed-off-by: Krzysztof Kozlowski krzysztof.kozlowski@linaro.org Link: https://lore.kernel.org/r/20220419113435.246203-2-krzysztof.kozlowski@linaro... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Lee Jones lee@kernel.org --- drivers/base/driver.c | 69 +++++++++++++++++++++++++++++++++ drivers/base/platform.c | 28 ++----------- include/linux/device/driver.h | 2 + include/linux/platform_device.h | 6 ++- 4 files changed, 80 insertions(+), 25 deletions(-)
diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 8c0d33e182fd5..1b9d47b10bd0a 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -30,6 +30,75 @@ static struct device *next_device(struct klist_iter *i) return dev; }
+/** + * driver_set_override() - Helper to set or clear driver override. + * @dev: Device to change + * @override: Address of string to change (e.g. &device->driver_override); + * The contents will be freed and hold newly allocated override. + * @s: NUL-terminated string, new driver name to force a match, pass empty + * string to clear it ("" or "\n", where the latter is only for sysfs + * interface). + * @len: length of @s + * + * Helper to set or clear driver override in a device, intended for the cases + * when the driver_override field is allocated by driver/bus code. + * + * Returns: 0 on success or a negative error code on failure. + */ +int driver_set_override(struct device *dev, const char **override, + const char *s, size_t len) +{ + const char *new, *old; + char *cp; + + if (!override || !s) + return -EINVAL; + + /* + * The stored value will be used in sysfs show callback (sysfs_emit()), + * which has a length limit of PAGE_SIZE and adds a trailing newline. + * Thus we can store one character less to avoid truncation during sysfs + * show. + */ + if (len >= (PAGE_SIZE - 1)) + return -EINVAL; + + if (!len) { + /* Empty string passed - clear override */ + device_lock(dev); + old = *override; + *override = NULL; + device_unlock(dev); + kfree(old); + + return 0; + } + + cp = strnchr(s, len, '\n'); + if (cp) + len = cp - s; + + new = kstrndup(s, len, GFP_KERNEL); + if (!new) + return -ENOMEM; + + device_lock(dev); + old = *override; + if (cp != s) { + *override = new; + } else { + /* "\n" passed - clear override */ + kfree(new); + *override = NULL; + } + device_unlock(dev); + + kfree(old); + + return 0; +} +EXPORT_SYMBOL_GPL(driver_set_override); + /** * driver_for_each_device - Iterator for devices bound to a driver. * @drv: Driver we're iterating. diff --git a/drivers/base/platform.c b/drivers/base/platform.c index ac5cf1a8d79ab..596fbe6b701a5 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -1270,31 +1270,11 @@ static ssize_t driver_override_store(struct device *dev, const char *buf, size_t count) { struct platform_device *pdev = to_platform_device(dev); - char *driver_override, *old, *cp; - - /* We need to keep extra room for a newline */ - if (count >= (PAGE_SIZE - 1)) - return -EINVAL; - - driver_override = kstrndup(buf, count, GFP_KERNEL); - if (!driver_override) - return -ENOMEM; - - cp = strchr(driver_override, '\n'); - if (cp) - *cp = '\0'; - - device_lock(dev); - old = pdev->driver_override; - if (strlen(driver_override)) { - pdev->driver_override = driver_override; - } else { - kfree(driver_override); - pdev->driver_override = NULL; - } - device_unlock(dev); + int ret;
- kfree(old); + ret = driver_set_override(dev, &pdev->driver_override, buf, count); + if (ret) + return ret;
return count; } diff --git a/include/linux/device/driver.h b/include/linux/device/driver.h index a498ebcf49933..abf948e102f5d 100644 --- a/include/linux/device/driver.h +++ b/include/linux/device/driver.h @@ -150,6 +150,8 @@ extern int __must_check driver_create_file(struct device_driver *driver, extern void driver_remove_file(struct device_driver *driver, const struct driver_attribute *attr);
+int driver_set_override(struct device *dev, const char **override, + const char *s, size_t len); extern int __must_check driver_for_each_device(struct device_driver *drv, struct device *start, void *data, diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 8aefdc0099c86..72cf70857b85f 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -31,7 +31,11 @@ struct platform_device { struct resource *resource;
const struct platform_device_id *id_entry; - char *driver_override; /* Driver name to force a match */ + /* + * Driver name to force a match. Do not set directly, because core + * frees it. Use driver_set_override() to set or clear it. + */ + const char *driver_override;
/* MFD cell pointer */ struct mfd_cell *mfd_cell;
From: Krzysztof Kozlowski krzysztof.kozlowski@linaro.org
commit e5f89131a06142e91073b6959d91cea73861d40e upstream.
Memory pointed by variable 'old' in field store macro is not modified, so it can be made a pointer to const.
Signed-off-by: Krzysztof Kozlowski krzysztof.kozlowski@linaro.org Link: https://lore.kernel.org/r/20220419113435.246203-12-krzysztof.kozlowski@linar... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Lee Jones lee@kernel.org --- drivers/rpmsg/rpmsg_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index a71de08acc7b9..c544dee0b5dd9 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -376,7 +376,8 @@ field##_store(struct device *dev, struct device_attribute *attr, \ const char *buf, size_t sz) \ { \ struct rpmsg_device *rpdev = to_rpmsg_device(dev); \ - char *new, *old; \ + const char *old; \ + char *new; \ \ new = kstrndup(buf, sz, GFP_KERNEL); \ if (!new) \
From: Krzysztof Kozlowski krzysztof.kozlowski@linaro.org
commit 42cd402b8fd4672b692400fe5f9eecd55d2794ac upstream.
The driver_override field from platform driver should not be initialized from static memory (string literal) because the core later kfree() it, for example when driver_override is set via sysfs.
Use dedicated helper to set driver_override properly.
Fixes: 950a7388f02b ("rpmsg: Turn name service into a stand alone driver") Fixes: c0cdc19f84a4 ("rpmsg: Driver for user space endpoint interface") Reviewed-by: Bjorn Andersson bjorn.andersson@linaro.org Signed-off-by: Krzysztof Kozlowski krzysztof.kozlowski@linaro.org Link: https://lore.kernel.org/r/20220419113435.246203-13-krzysztof.kozlowski@linar... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Lee Jones lee@kernel.org --- drivers/rpmsg/rpmsg_internal.h | 13 +++++++++++-- include/linux/rpmsg.h | 6 ++++-- 2 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h index a76c344253bf5..5f4f3691bbf1e 100644 --- a/drivers/rpmsg/rpmsg_internal.h +++ b/drivers/rpmsg/rpmsg_internal.h @@ -90,10 +90,19 @@ int rpmsg_release_channel(struct rpmsg_device *rpdev, */ static inline int rpmsg_chrdev_register_device(struct rpmsg_device *rpdev) { + int ret; + strcpy(rpdev->id.name, "rpmsg_chrdev"); - rpdev->driver_override = "rpmsg_chrdev"; + ret = driver_set_override(&rpdev->dev, &rpdev->driver_override, + rpdev->id.name, strlen(rpdev->id.name)); + if (ret) + return ret; + + ret = rpmsg_register_device(rpdev); + if (ret) + kfree(rpdev->driver_override);
- return rpmsg_register_device(rpdev); + return ret; }
#endif diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h index a8dcf8a9ae885..1b7294cefb807 100644 --- a/include/linux/rpmsg.h +++ b/include/linux/rpmsg.h @@ -41,7 +41,9 @@ struct rpmsg_channel_info { * rpmsg_device - device that belong to the rpmsg bus * @dev: the device struct * @id: device id (used to match between rpmsg drivers and devices) - * @driver_override: driver name to force a match + * @driver_override: driver name to force a match; do not set directly, + * because core frees it; use driver_set_override() to + * set or clear it. * @src: local address * @dst: destination address * @ept: the rpmsg endpoint of this channel @@ -51,7 +53,7 @@ struct rpmsg_channel_info { struct rpmsg_device { struct device dev; struct rpmsg_device_id id; - char *driver_override; + const char *driver_override; u32 src; u32 dst; struct rpmsg_endpoint *ept;
On Wed, Oct 18, 2023 at 01:04:34PM +0100, Lee Jones wrote:
From: Krzysztof Kozlowski krzysztof.kozlowski@linaro.org
commit 42cd402b8fd4672b692400fe5f9eecd55d2794ac upstream.
The driver_override field from platform driver should not be initialized from static memory (string literal) because the core later kfree() it, for example when driver_override is set via sysfs.
Use dedicated helper to set driver_override properly.
Fixes: 950a7388f02b ("rpmsg: Turn name service into a stand alone driver") Fixes: c0cdc19f84a4 ("rpmsg: Driver for user space endpoint interface") Reviewed-by: Bjorn Andersson bjorn.andersson@linaro.org Signed-off-by: Krzysztof Kozlowski krzysztof.kozlowski@linaro.org Link: https://lore.kernel.org/r/20220419113435.246203-13-krzysztof.kozlowski@linar... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Lee Jones lee@kernel.org
drivers/rpmsg/rpmsg_internal.h | 13 +++++++++++-- include/linux/rpmsg.h | 6 ++++-- 2 files changed, 15 insertions(+), 4 deletions(-)
Any specific reason why you missed the fixes for this commit as well? Turned out to need some more things after this :(
Why are these needed at all for the stable kernels anyway? It's good to have in the tree, but who is using manual overrides for the rpmsg driver in the first place?
I'm going to drop all of these from the stable queues now and wait for a fixed up set of patches with a good reason to justify their existence in the stable trees.
thanks,
greg k-h
On Mon, 23 Oct 2023, Greg Kroah-Hartman wrote:
On Wed, Oct 18, 2023 at 01:04:34PM +0100, Lee Jones wrote:
From: Krzysztof Kozlowski krzysztof.kozlowski@linaro.org
commit 42cd402b8fd4672b692400fe5f9eecd55d2794ac upstream.
The driver_override field from platform driver should not be initialized from static memory (string literal) because the core later kfree() it, for example when driver_override is set via sysfs.
Use dedicated helper to set driver_override properly.
Fixes: 950a7388f02b ("rpmsg: Turn name service into a stand alone driver") Fixes: c0cdc19f84a4 ("rpmsg: Driver for user space endpoint interface") Reviewed-by: Bjorn Andersson bjorn.andersson@linaro.org Signed-off-by: Krzysztof Kozlowski krzysztof.kozlowski@linaro.org Link: https://lore.kernel.org/r/20220419113435.246203-13-krzysztof.kozlowski@linar... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Lee Jones lee@kernel.org
drivers/rpmsg/rpmsg_internal.h | 13 +++++++++++-- include/linux/rpmsg.h | 6 ++++-- 2 files changed, 15 insertions(+), 4 deletions(-)
Any specific reason why you missed the fixes for this commit as well? Turned out to need some more things after this :(
No reason not to. I didn't notice them.
Why are these needed at all for the stable kernels anyway? It's good to have in the tree, but who is using manual overrides for the rpmsg driver in the first place?
UAF.
I'm going to drop all of these from the stable queues now and wait for a fixed up set of patches with a good reason to justify their existence in the stable trees.
As per our SOP, I'd like to avoid spelling it out.
Ping me for details if you really want to know.
Which patches have you dropped? Just these 3 or all branches?
On Mon, Oct 23, 2023 at 10:39:03AM +0100, Lee Jones wrote:
On Mon, 23 Oct 2023, Greg Kroah-Hartman wrote:
On Wed, Oct 18, 2023 at 01:04:34PM +0100, Lee Jones wrote:
From: Krzysztof Kozlowski krzysztof.kozlowski@linaro.org
commit 42cd402b8fd4672b692400fe5f9eecd55d2794ac upstream.
The driver_override field from platform driver should not be initialized from static memory (string literal) because the core later kfree() it, for example when driver_override is set via sysfs.
Use dedicated helper to set driver_override properly.
Fixes: 950a7388f02b ("rpmsg: Turn name service into a stand alone driver") Fixes: c0cdc19f84a4 ("rpmsg: Driver for user space endpoint interface") Reviewed-by: Bjorn Andersson bjorn.andersson@linaro.org Signed-off-by: Krzysztof Kozlowski krzysztof.kozlowski@linaro.org Link: https://lore.kernel.org/r/20220419113435.246203-13-krzysztof.kozlowski@linar... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Lee Jones lee@kernel.org
drivers/rpmsg/rpmsg_internal.h | 13 +++++++++++-- include/linux/rpmsg.h | 6 ++++-- 2 files changed, 15 insertions(+), 4 deletions(-)
Any specific reason why you missed the fixes for this commit as well? Turned out to need some more things after this :(
No reason not to. I didn't notice them.
fixes for fixes are important :)
Which patches have you dropped? Just these 3 or all branches?
All branches.
thanks,
greg k-h
linux-stable-mirror@lists.linaro.org