mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "Peng Fan (OSS)" <peng.fan@oss.nxp.com>
To: 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>
Cc: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>,
	 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: [PATCH 1/4] software node: do not resurrect swnode being released
Date: Wed, 04 Nov 2026 23:43:13 +0800	[thread overview]
Message-ID: <20261104-swnode-race-v1-v1-1-9406a67e25be@nxp.com> (raw)
In-Reply-To: <20261104-swnode-race-v1-v1-0-9406a67e25be@nxp.com>

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


  reply	other threads:[~2026-11-04 15:38 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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) [this message]
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)

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=20261104-swnode-race-v1-v1-1-9406a67e25be@nxp.com \
    --to=peng.fan@oss.nxp.com \
    --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=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®