* [PATCH 0/4] software node: fix lookup of swnode in release path
@ 2026-11-04 15:43 Peng Fan (OSS)
2026-11-04 15:43 ` [PATCH 1/4] software node: do not resurrect swnode being released Peng Fan (OSS)
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Peng Fan (OSS) @ 2026-11-04 15:43 UTC (permalink / raw)
To: Andy Shevchenko, Daniel Scally, Heikki Krogerus, Sakari Ailus,
Bartosz Golaszewski, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Johan Hovold
Cc: Rafael J. Wysocki, linux-acpi, driver-core, linux-kernel, imx, Peng Fan
This series fixes a software node lifetime bug where a lookup can find an
swnode whose kobject refcount has already reached zero.
The kset list keeps an swnode linked until kobject_cleanup() calls
kobj_kset_leave(), which happens after the last kobject_put() has already
dropped the refcount to zero. A lookup that does not check whether the
object is still referenceable can therefore call kobject_get() on a dead
object, triggering:
refcount_t: addition on 0; use-after-free
refcount_t: saturated; leaking memory
refcount_t: underflow; use-after-free
The problem is easily reproduced on i.MX95 with dwc3-imx when
dwc3_core_probe() returns -EPROBE_DEFER. dwc3_imx_probe() removes its
static software node from the device and the deferred probe retry
immediately adds the same node again. With
CONFIG_DEBUG_KOBJECT_RELEASE the old swnode stays on the kset list for
some time after its refcount reaches zero, so the retry reliably finds
it.
The first patch fixes the pointer-based kset lookup by using
kobject_get_unless_zero() and skipping entries that are already in their
release path. It also makes registration return the newly created swnode
reference so that device_add_software_node() does not have to look the
node up again after registering it. Reference-owning callers get a
reference from the lookup, while software_node_fwnode() keeps its
borrowed-pointer semantics by dropping the temporary lookup reference.
The remaining patches harden related weak lookups for the same class of
problem. The series is organized as follows:
1. Fix the pointer-based kset lookup and avoid the second lookup after
registration.
2. Harden software_node_find_by_name() against dying entries.
3. Check the result of software_node_fwnode() in the graph
remote-endpoint path.
4. Skip dying children when walking the parent's children list.
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
Peng Fan (4):
software node: do not resurrect swnode being released
software node: use kobject_get_unless_zero() in software_node_find_by_name()
software node: handle missing remote endpoint node
software node: skip dying children during traversal
drivers/base/swnode.c | 143 +++++++++++++++++++++++++++++++++++---------------
1 file changed, 100 insertions(+), 43 deletions(-)
---
base-commit: 2637bdec0535a2044cd830eba9a79363651d9043
change-id: 20261104-swnode-race-v1-b9f2dcc954c8
Best regards,
--
Peng Fan <peng.fan@nxp.com>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/4] software node: do not resurrect swnode being released
2026-11-04 15:43 [PATCH 0/4] software node: fix lookup of swnode in release path Peng Fan (OSS)
@ 2026-11-04 15:43 ` Peng Fan (OSS)
2026-11-04 15:43 ` [PATCH 2/4] software node: use kobject_get_unless_zero() in software_node_find_by_name() Peng Fan (OSS)
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Peng Fan (OSS) @ 2026-11-04 15:43 UTC (permalink / raw)
To: Andy Shevchenko, Daniel Scally, Heikki Krogerus, Sakari Ailus,
Bartosz Golaszewski, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Johan Hovold
Cc: Rafael J. Wysocki, linux-acpi, driver-core, linux-kernel, imx, Peng Fan
From: Peng Fan <peng.fan@nxp.com>
software_node_to_swnode() walks the kset list under list_lock, but the
last kobject_put() drops the reference count to zero before
kobject_cleanup() removes the object from the list. A lookup can
therefore find a zero-ref swnode and call kobject_get() on it, which
triggers refcount warnings and a use-after-free.
One way to hit this is the dwc3-imx driver on i.MX95: when
dwc3_core_probe() returns -EPROBE_DEFER, dwc3_imx_probe() removes the
static software node from the device, and the deferred probe retry
immediately adds the same node again. The previous swnode is still
waiting for its release callback, and CONFIG_DEBUG_KOBJECT_RELEASE makes
the window large and deterministic.
Fix the lookup by acquiring references with kobject_get_unless_zero()
and skipping objects that are already in their release path. Use a
reference-returning variant for callers that keep the node (register,
unregister and device_add_software_node()) and keep a borrowed-pointer
variant for software_node_fwnode().
To avoid looking the node up again after registration,
software_node_register() now uses an internal helper that returns the
newly created swnode reference, and device_add_software_node() consumes
that reference directly.
Fixes: 80488a6b1d3c ("software node: Add support for static node descriptors")
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
drivers/base/swnode.c | 114 ++++++++++++++++++++++++++++++++++----------------
1 file changed, 78 insertions(+), 36 deletions(-)
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index 84a77ffa3dc4b..19a4153f3fac1 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -75,8 +75,28 @@ static inline struct swnode *dev_to_swnode(struct device *dev)
return to_swnode(fwnode);
}
+static struct swnode *swnode_get(struct swnode *swnode)
+{
+ kobject_get(&swnode->kobj);
+
+ return swnode;
+}
+
+static void swnode_put(struct swnode *swnode)
+{
+ kobject_put(&swnode->kobj);
+}
+
+/*
+ * Find the swnode registered for @node and return it with a new reference.
+ *
+ * A kobject remains on the kset list until its release callback runs, so an
+ * entry whose last reference has already been dropped can still be found
+ * here. Use kobject_get_unless_zero() to skip such objects instead of
+ * resurrecting them.
+ */
static struct swnode *
-software_node_to_swnode(const struct software_node *node)
+software_node_to_swnode_get(const struct software_node *node)
{
struct swnode *swnode = NULL;
struct kobject *k;
@@ -87,10 +107,16 @@ software_node_to_swnode(const struct software_node *node)
spin_lock(&swnode_kset->list_lock);
list_for_each_entry(k, &swnode_kset->list, entry) {
- swnode = kobj_to_swnode(k);
- if (swnode->node == node)
- break;
- swnode = NULL;
+ struct swnode *candidate = kobj_to_swnode(k);
+
+ if (candidate->node != node)
+ continue;
+
+ if (!kobject_get_unless_zero(k))
+ continue;
+
+ swnode = candidate;
+ break;
}
spin_unlock(&swnode_kset->list_lock);
@@ -98,6 +124,21 @@ software_node_to_swnode(const struct software_node *node)
return swnode;
}
+/*
+ * Borrowed-pointer variant of software_node_to_swnode_get(). The caller must
+ * guarantee the lifetime of @node by other means.
+ */
+static struct swnode *
+software_node_to_swnode(const struct software_node *node)
+{
+ struct swnode *swnode = software_node_to_swnode_get(node);
+
+ if (swnode)
+ swnode_put(swnode);
+
+ return swnode;
+}
+
const struct software_node *to_software_node(const struct fwnode_handle *fwnode)
{
const struct swnode *swnode = to_swnode(fwnode);
@@ -374,18 +415,6 @@ EXPORT_SYMBOL_GPL(property_entries_free);
/* -------------------------------------------------------------------------- */
/* fwnode operations */
-static struct swnode *swnode_get(struct swnode *swnode)
-{
- kobject_get(&swnode->kobj);
-
- return swnode;
-}
-
-static void swnode_put(struct swnode *swnode)
-{
- kobject_put(&swnode->kobj);
-}
-
static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode)
{
struct swnode *swnode = swnode_get(to_swnode(fwnode));
@@ -977,21 +1006,39 @@ void software_node_unregister_node_group(const struct software_node * const *nod
}
EXPORT_SYMBOL_GPL(software_node_unregister_node_group);
+static struct swnode *
+software_node_register_node(const struct software_node *node)
+{
+ struct swnode *parent, *swnode;
+ struct fwnode_handle *fwnode;
+
+ swnode = software_node_to_swnode_get(node);
+ if (swnode) {
+ swnode_put(swnode);
+ return ERR_PTR(-EEXIST);
+ }
+
+ parent = software_node_to_swnode_get(node->parent);
+ if (node->parent && !parent)
+ return ERR_PTR(-EINVAL);
+
+ fwnode = swnode_register(node, parent, 0);
+ if (parent)
+ swnode_put(parent);
+
+ if (IS_ERR(fwnode))
+ return ERR_CAST(fwnode);
+
+ return to_swnode(fwnode);
+}
+
/**
* software_node_register - Register static software node
* @node: The software node to be registered
*/
int software_node_register(const struct software_node *node)
{
- struct swnode *parent = software_node_to_swnode(node->parent);
-
- if (software_node_to_swnode(node))
- return -EEXIST;
-
- if (node->parent && !parent)
- return -EINVAL;
-
- return PTR_ERR_OR_ZERO(swnode_register(node, parent, 0));
+ return PTR_ERR_OR_ZERO(software_node_register_node(node));
}
EXPORT_SYMBOL_GPL(software_node_register);
@@ -1061,21 +1108,16 @@ EXPORT_SYMBOL_GPL(fwnode_remove_software_node);
int device_add_software_node(struct device *dev, const struct software_node *node)
{
struct swnode *swnode;
- int ret;
/* Only one software node per device. */
if (dev_to_swnode(dev))
return -EBUSY;
- swnode = software_node_to_swnode(node);
- if (swnode) {
- swnode_get(swnode);
- } else {
- ret = software_node_register(node);
- if (ret)
- return ret;
-
- swnode = software_node_to_swnode(node);
+ swnode = software_node_to_swnode_get(node);
+ if (!swnode) {
+ swnode = software_node_register_node(node);
+ if (IS_ERR(swnode))
+ return PTR_ERR(swnode);
}
set_secondary_fwnode(dev, &swnode->fwnode);
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/4] software node: use kobject_get_unless_zero() in software_node_find_by_name()
2026-11-04 15:43 [PATCH 0/4] software node: fix lookup of swnode in release path Peng Fan (OSS)
2026-11-04 15:43 ` [PATCH 1/4] software node: do not resurrect swnode being released Peng Fan (OSS)
@ 2026-11-04 15:43 ` Peng Fan (OSS)
2026-11-04 21:29 ` Andy Shevchenko
2026-11-04 15:43 ` [PATCH 3/4] software node: handle missing remote endpoint node Peng Fan (OSS)
2026-11-04 15:43 ` [PATCH 4/4] software node: skip dying children during traversal Peng Fan (OSS)
3 siblings, 1 reply; 6+ messages in thread
From: Peng Fan (OSS) @ 2026-11-04 15:43 UTC (permalink / raw)
To: Andy Shevchenko, Daniel Scally, Heikki Krogerus, Sakari Ailus,
Bartosz Golaszewski, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Johan Hovold
Cc: Rafael J. Wysocki, linux-acpi, driver-core, linux-kernel, imx, Peng Fan
From: Peng Fan <peng.fan@nxp.com>
software_node_find_by_name() also walks the swnode kset list and takes a
reference with swnode_get(). Like the pointer-based lookup, it can
encounter an entry whose refcount has already reached zero but which is
still linked until its delayed release callback runs.
Use kobject_get_unless_zero() and continue scanning when the entry is
already dying.
Fixes: 80488a6b1d3c ("software node: Add support for static node descriptors")
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
drivers/base/swnode.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index 19a4153f3fac1..9095d50ba146f 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -831,8 +831,8 @@ software_node_find_by_name(const struct software_node *parent, const char *name)
swnode = kobj_to_swnode(k);
if (parent == swnode->node->parent && swnode->node->name &&
!strcmp(name, swnode->node->name)) {
- swnode_get(swnode);
- break;
+ if (kobject_get_unless_zero(k))
+ break;
}
swnode = NULL;
}
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 3/4] software node: handle missing remote endpoint node
2026-11-04 15:43 [PATCH 0/4] software node: fix lookup of swnode in release path Peng Fan (OSS)
2026-11-04 15:43 ` [PATCH 1/4] software node: do not resurrect swnode being released Peng Fan (OSS)
2026-11-04 15:43 ` [PATCH 2/4] software node: use kobject_get_unless_zero() in software_node_find_by_name() Peng Fan (OSS)
@ 2026-11-04 15:43 ` Peng Fan (OSS)
2026-11-04 15:43 ` [PATCH 4/4] software node: skip dying children during traversal Peng Fan (OSS)
3 siblings, 0 replies; 6+ messages in thread
From: Peng Fan (OSS) @ 2026-11-04 15:43 UTC (permalink / raw)
To: Andy Shevchenko, Daniel Scally, Heikki Krogerus, Sakari Ailus,
Bartosz Golaszewski, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Johan Hovold
Cc: Rafael J. Wysocki, linux-acpi, driver-core, linux-kernel, imx, Peng Fan
From: Peng Fan <peng.fan@nxp.com>
software_node_graph_get_remote_endpoint() passes the result of
software_node_fwnode() directly to software_node_get(). That lookup
returns NULL if the referenced software node is not registered, and now
also if it is in its release path.
Check the result and return NULL instead of passing a NULL fwnode to
software_node_get().
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
drivers/base/swnode.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index 9095d50ba146f..4d95a59a27f0a 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -676,6 +676,7 @@ software_node_graph_get_remote_endpoint(const struct fwnode_handle *fwnode)
{
struct swnode *swnode = to_swnode(fwnode);
const struct software_node_ref_args *ref;
+ struct fwnode_handle *refnode;
const struct property_entry *prop;
if (!swnode)
@@ -690,7 +691,11 @@ software_node_graph_get_remote_endpoint(const struct fwnode_handle *fwnode)
if (!ref->swnode)
return NULL;
- return software_node_get(software_node_fwnode(ref->swnode));
+ refnode = software_node_fwnode(ref->swnode);
+ if (!refnode)
+ return NULL;
+
+ return software_node_get(refnode);
}
static struct fwnode_handle *
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 4/4] software node: skip dying children during traversal
2026-11-04 15:43 [PATCH 0/4] software node: fix lookup of swnode in release path Peng Fan (OSS)
` (2 preceding siblings ...)
2026-11-04 15:43 ` [PATCH 3/4] software node: handle missing remote endpoint node Peng Fan (OSS)
@ 2026-11-04 15:43 ` Peng Fan (OSS)
3 siblings, 0 replies; 6+ messages in thread
From: Peng Fan (OSS) @ 2026-11-04 15:43 UTC (permalink / raw)
To: Andy Shevchenko, Daniel Scally, Heikki Krogerus, Sakari Ailus,
Bartosz Golaszewski, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Johan Hovold
Cc: Rafael J. Wysocki, linux-acpi, driver-core, linux-kernel, imx, Peng Fan
From: Peng Fan <peng.fan@nxp.com>
A child swnode is removed from its parent's children list in
software_node_release(), which runs after its kobject refcount has
already reached zero. The fwnode child traversal helpers can therefore
find an entry that is in its release path and call kobject_get() on a
zero refcount.
Use kobject_get_unless_zero() in software_node_get_next_child() and
software_node_get_named_child_node() and keep scanning until a live
child is found or the list ends.
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
drivers/base/swnode.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index 4d95a59a27f0a..1354447069e10 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -515,7 +515,18 @@ software_node_get_next_child(const struct fwnode_handle *fwnode,
c = list_first_entry(&p->children, struct swnode, entry);
fwnode_handle_put(child);
- return fwnode_handle_get(&c->fwnode);
+
+ for (;;) {
+ if (kobject_get_unless_zero(&c->kobj))
+ return &c->fwnode;
+
+ if (list_is_last(&c->entry, &p->children))
+ break;
+
+ c = list_next_entry(c, entry);
+ }
+
+ return NULL;
}
static struct fwnode_handle *
@@ -529,10 +540,9 @@ software_node_get_named_child_node(const struct fwnode_handle *fwnode,
return NULL;
list_for_each_entry(child, &swnode->children, entry) {
- if (!strcmp(childname, kobject_name(&child->kobj))) {
- swnode_get(child);
+ if (!strcmp(childname, kobject_name(&child->kobj)) &&
+ kobject_get_unless_zero(&child->kobj))
return &child->fwnode;
- }
}
return NULL;
}
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 2/4] software node: use kobject_get_unless_zero() in software_node_find_by_name()
2026-11-04 15:43 ` [PATCH 2/4] software node: use kobject_get_unless_zero() in software_node_find_by_name() Peng Fan (OSS)
@ 2026-11-04 21:29 ` Andy Shevchenko
0 siblings, 0 replies; 6+ messages in thread
From: Andy Shevchenko @ 2026-11-04 21:29 UTC (permalink / raw)
To: Peng Fan (OSS)
Cc: Daniel Scally, Heikki Krogerus, Sakari Ailus,
Bartosz Golaszewski, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Johan Hovold, Rafael J. Wysocki, linux-acpi,
driver-core, linux-kernel, imx, Peng Fan
On Wed, Nov 04, 2026 at 11:43:14PM +0800, Peng Fan (OSS) wrote:
> software_node_find_by_name() also walks the swnode kset list and takes a
> reference with swnode_get(). Like the pointer-based lookup, it can
> encounter an entry whose refcount has already reached zero but which is
> still linked until its delayed release callback runs.
>
> Use kobject_get_unless_zero() and continue scanning when the entry is
> already dying.
...
> if (parent == swnode->node->parent && swnode->node->name &&
> !strcmp(name, swnode->node->name)) {
> - swnode_get(swnode);
> - break;
> + if (kobject_get_unless_zero(k))
> + break;
Can we rather see even in the previous patch something like
... swnode_get_alive(...)
{
...uses kobject_get_unless_zero()...
}
?
> }
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-11-04 21:29 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-04 15:43 [PATCH 0/4] software node: fix lookup of swnode in release path Peng Fan (OSS)
2026-11-04 15:43 ` [PATCH 1/4] software node: do not resurrect swnode being released Peng Fan (OSS)
2026-11-04 15:43 ` [PATCH 2/4] software node: use kobject_get_unless_zero() in software_node_find_by_name() Peng Fan (OSS)
2026-11-04 21:29 ` Andy Shevchenko
2026-11-04 15:43 ` [PATCH 3/4] software node: handle missing remote endpoint node Peng Fan (OSS)
2026-11-04 15:43 ` [PATCH 4/4] software node: skip dying children during traversal Peng Fan (OSS)
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®