| From dd41e14b5574cc25ce54b79fb587b72ac97daa86 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 23 Jun 2021 16:14:21 +0300 |
| Subject: software node: Handle software node injection to an existing device |
| properly |
| |
| From: Heikki Krogerus <heikki.krogerus@linux.intel.com> |
| |
| [ Upstream commit 5dca69e26fe97f17d4a6cbd6872103c868577b14 ] |
| |
| The function software_node_notify() - the function that creates |
| and removes the symlinks between the node and the device - was |
| called unconditionally in device_add_software_node() and |
| device_remove_software_node(), but it needs to be called in |
| those functions only in the special case where the node is |
| added to a device that has already been registered. |
| |
| This fixes NULL pointer dereference that happens if |
| device_remove_software_node() is used with device that was |
| never registered. |
| |
| Fixes: b622b24519f5 ("software node: Allow node addition to already existing device") |
| Reported-and-tested-by: Dominik Brodowski <linux@dominikbrodowski.net> |
| Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> |
| Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> |
| Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/base/swnode.c | 16 ++++++++++++---- |
| 1 file changed, 12 insertions(+), 4 deletions(-) |
| |
| diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c |
| index 88310ac9ce90..62c536f9d925 100644 |
| --- a/drivers/base/swnode.c |
| +++ b/drivers/base/swnode.c |
| @@ -1032,7 +1032,15 @@ int device_add_software_node(struct device *dev, const struct software_node *nod |
| } |
| |
| set_secondary_fwnode(dev, &swnode->fwnode); |
| - software_node_notify(dev, KOBJ_ADD); |
| + |
| + /* |
| + * If the device has been fully registered by the time this function is |
| + * called, software_node_notify() must be called separately so that the |
| + * symlinks get created and the reference count of the node is kept in |
| + * balance. |
| + */ |
| + if (device_is_registered(dev)) |
| + software_node_notify(dev, KOBJ_ADD); |
| |
| return 0; |
| } |
| @@ -1052,7 +1060,8 @@ void device_remove_software_node(struct device *dev) |
| if (!swnode) |
| return; |
| |
| - software_node_notify(dev, KOBJ_REMOVE); |
| + if (device_is_registered(dev)) |
| + software_node_notify(dev, KOBJ_REMOVE); |
| set_secondary_fwnode(dev, NULL); |
| kobject_put(&swnode->kobj); |
| } |
| @@ -1106,8 +1115,7 @@ int software_node_notify(struct device *dev, unsigned long action) |
| |
| switch (action) { |
| case KOBJ_ADD: |
| - ret = sysfs_create_link_nowarn(&dev->kobj, &swnode->kobj, |
| - "software_node"); |
| + ret = sysfs_create_link(&dev->kobj, &swnode->kobj, "software_node"); |
| if (ret) |
| break; |
| |
| -- |
| 2.30.2 |
| |