[PATCH 1/1] sofware node: Only the managing device can unreference managed software node
From: mike . isely
Date: Tue Feb 24 2026 - 14:25:13 EST
From: Mike Isely <mike.isely@xxxxxxxxxxxxxxxxx>
A scenario exists where device_create_managed_software_node() is used
to create an swnode instance that will be implicitly shared to a child
device despite best intentions not to permit such sharing (per the
comment in device_create_managed_software_node()). I encountered this
with the sfp kernel module when it was instantiated with properties
via a call to platform_device_register_full() - it will create hwmon
child devices which get all property references. Unfortunately with
just a "managed" boolean in struct swnode handling this, then
kobject_put() gets called for the managed aspect when the child device
goes away instead of the parent. This leads to premature freeing of
the swnode structure, followed by use-after-free problems, heap
corruption, and generally chaos / crashes / misbehavior in the kernel.
This commit changes that boolean into a pointer to the actual managing
struct device, which is then checked against the struct device
instance that is actually going away (via the usual call back into
software_node_notify_remove()). Thus the child device removal is
ignored as it should, and we only do the kobject_put() when the actual
managing struct device instance goes away. We effectively carry a
little bit more information now so that we can be sure to clean up
only when the correct struct device instance is actually going away.
Note that while we are now keeping a pointer to a struct device here,
this is safe to do because the pointer itself only stays in use while
the pointed-to device remains valid. (So no need to be concerned
about additional reference counting.)
Signed-off-by: Mike Isely <isely@xxxxxxxxx>
---
drivers/base/swnode.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index 16a8301c25d63..2ead8e4062f63 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -36,8 +36,8 @@ struct swnode {
struct list_head children;
struct swnode *parent;
+ struct device *managing_dev;
unsigned int allocated:1;
- unsigned int managed:1;
};
static DEFINE_IDA(swnode_root_ids);
@@ -1078,7 +1078,7 @@ int device_create_managed_software_node(struct device *dev,
if (IS_ERR(fwnode))
return PTR_ERR(fwnode);
- to_swnode(fwnode)->managed = true;
+ to_swnode(fwnode)->managing_dev = dev;
set_secondary_fwnode(dev, fwnode);
if (device_is_registered(dev))
@@ -1121,7 +1121,7 @@ void software_node_notify_remove(struct device *dev)
sysfs_remove_link(&dev->kobj, "software_node");
kobject_put(&swnode->kobj);
- if (swnode->managed) {
+ if (swnode->managing_dev == dev) {
set_secondary_fwnode(dev, NULL);
kobject_put(&swnode->kobj);
}
--
2.47.3