mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Peng Fan <peng.fan@linux.dev>
To: "Peng Fan (OSS)" <peng.fan@oss.nxp.com>,
	Andy Shevchenko <andriy.shevchenko@linux.intel.com>,
	Daniel Scally <djrscally@gmail.com>,
	Heikki Krogerus <heikki.krogerus@linux.intel.com>,
	Sakari Ailus <sakari.ailus@linux.intel.com>,
	Bartosz Golaszewski <brgl@kernel.org>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	"Rafael J. Wysocki" <rafael@kernel.org>,
	Danilo Krummrich <dakr@kernel.org>,
	Johan Hovold <johan@kernel.org>,
	"Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
Cc: linux-acpi@vger.kernel.org, driver-core@lists.linux.dev,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	Peng Fan <peng.fan@nxp.com>
Subject: Re: [PATCH v2 1/4] software node: do not resurrect swnode being released
Date: Fri, 6 Nov 2026 16:57:14 +0800	[thread overview]
Message-ID: <au2WakZdbqc6asKF@fedora> (raw)
In-Reply-To: <20261106-swnode-race-v1-v2-1-f8bc7b14d578@nxp.com>

Hi All,
On Fri, Nov 06, 2026 at 12:38:35PM +0800, Peng Fan (OSS) wrote:
>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.

sashiko reported issue here:
https://lore.kernel.org/all/sashiko-outbox-200773@kernel.org/

But this is not a new issue introduced by this series. The series only fixes
the zero-reference lookup on swnode_kset->list for unnamed static nodes such
as dwc3_imx_swnode. Named-node and parent/child-group re-registration after
delayed release is a pre-existing limitation and is out of scope of this patchset.
The parent->children list also lacks synchronization between list_add_tail()
in swnode_register(), list_del() in software_node_release(), and child
traversal; that is why the child-traversal patch was dropped from this series.
We plan to address both in a separate patchset that adds proper locking/lifetime
handling for parent->children and parent/child-group reuse.

I tend to not introduce uneccessary churn to this patchset, but if you perfer
to have all the fixes ready, I could give a look to fix in v3.

Thanks
Peng

>
>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 and
>device_add_software_node()) and keep a borrowed-pointer variant for
>software_node_fwnode() and software_node_unregister().
>
>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 | 122 +++++++++++++++++++++++++++++++++++---------------
> 1 file changed, 86 insertions(+), 36 deletions(-)
>
>diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
>index 84a77ffa3dc4b..965e960156876 100644
>--- a/drivers/base/swnode.c
>+++ b/drivers/base/swnode.c
>@@ -75,8 +75,36 @@ 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);
>+}
>+
>+static struct swnode *swnode_get_unless_zero(struct swnode *swnode)
>+{
>+	if (!kobject_get_unless_zero(&swnode->kobj))
>+		return NULL;
>+
>+	return swnode;
>+}
>+
>+/*
>+ * 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 +115,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 (!swnode_get_unless_zero(candidate))
>+			continue;
>+
>+		swnode = candidate;
>+		break;
> 	}
> 
> 	spin_unlock(&swnode_kset->list_lock);
>@@ -98,6 +132,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 +423,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 +1014,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 +1116,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
>
>

  reply	other threads:[~2026-11-06  8:57 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-06  4:38 [PATCH v2 0/4] software node: fix lookup of swnode in release path Peng Fan (OSS)
2026-11-06  4:38 ` [PATCH v2 1/4] software node: do not resurrect swnode being released Peng Fan (OSS)
2026-11-06  8:57   ` Peng Fan [this message]
2026-11-06  4:38 ` [PATCH v2 2/4] software node: use kobject_get_unless_zero() in software_node_find_by_name() Peng Fan (OSS)
2026-11-06  4:38 ` [PATCH v2 3/4] software node: pin referenced swnodes while resolving them Peng Fan (OSS)
2026-11-06  4:38 ` [PATCH v2 4/4] software node: pin remote endpoint swnode Peng Fan (OSS)

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=au2WakZdbqc6asKF@fedora \
    --to=peng.fan@linux.dev \
    --cc=andriy.shevchenko@linux.intel.com \
    --cc=brgl@kernel.org \
    --cc=dakr@kernel.org \
    --cc=djrscally@gmail.com \
    --cc=driver-core@lists.linux.dev \
    --cc=gregkh@linuxfoundation.org \
    --cc=heikki.krogerus@linux.intel.com \
    --cc=imx@lists.linux.dev \
    --cc=johan@kernel.org \
    --cc=linux-acpi@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=peng.fan@nxp.com \
    --cc=peng.fan@oss.nxp.com \
    --cc=rafael.j.wysocki@intel.com \
    --cc=rafael@kernel.org \
    --cc=sakari.ailus@linux.intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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®