All of lore.kernel.org
 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
>
>

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

Thread overview: 9+ 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  4:52   ` sashiko-bot
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-09 21:13   ` Andy Shevchenko
2026-11-06  4:38 ` [PATCH v2 4/4] software node: pin remote endpoint swnode Peng Fan (OSS)
2026-11-09 21:14   ` Andy Shevchenko

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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.