The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH v4 0/3] device property: fix child iteration issues with secondary fwnodes
@ 2026-06-11 20:31 Andy Shevchenko
  2026-06-11 20:31 ` [PATCH v4 1/3] device property: fix infinite loop in fwnode_for_each_child_node() Andy Shevchenko
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Andy Shevchenko @ 2026-06-11 20:31 UTC (permalink / raw)
  To: Andy Shevchenko, Xu Yang, linux-acpi, driver-core, linux-kernel
  Cc: Daniel Scally, Heikki Krogerus, Sakari Ailus, Greg Kroah-Hartman,
	Rafael J. Wysocki, Danilo Krummrich

This series fixes two issues in the fwnode child iteration logic when
a secondary fwnode is present.

The first issue is  a refcount imbalance in software_node_get_next_child().
When a software node is used as a secondary fwnode, the iteration code may
incorrectly decrement the refcount of child nodes that do not belong to the
software node hierarchy. This results in refcount underflow and possible
use-after-free.

The second issue is an infinite loop in fwnode_for_each_child_node(), caused
by improper handling of iteration state across primary and secondary fwnodes.
When iterating over children from both primary and secondary fwnodes, the code
may incorrectly resume iteration from the primary fwnode even when the current
child belongs to the secondary, leading to repeated traversal and a loop.

Both issues are triggered when mixing different fwnode types through the
secondary mechanism, and stem from incorrect assumptions about ownership
and traversal context of child nodes.

Changes in v4:
- amended the fix and test case (Andy)
- added patch 2 to align other implementations with RAII approach (Andy)
- tested on Intel Galileo board for which the initial code was developed (Andy)
- Link to v3: https://patch.msgid.link/20260605-fixes_fwnode_iteration-v3-0-44c18472e1d1@nxp.com

Changes in v3:
- remove software node patch
- add a kunit test case suggested by Andy Shevchenko
- Link to v2: https://patch.msgid.link/20260603-fixes_fwnode_iteration-v2-0-0ae381f8b7b9@nxp.com

Changes in v2:
- use __free() to cleanup parent fwnode
- Link to v1: https://lore.kernel.org/r/20260525-fixes_fwnode_iteration-v1-0-a12903fb2919@nxp.com

Andy Shevchenko (1):
  device property: Refactor to use RAII approach

Xu Yang (2):
  device property: fix infinite loop in fwnode_for_each_child_node()
  device property: add test cases for fwnode_for_each_child_node()

 drivers/base/property.c                 |  41 ++++---
 drivers/base/test/Kconfig               |   1 +
 drivers/base/test/property-entry-test.c | 136 ++++++++++++++++++++++++
 3 files changed, 161 insertions(+), 17 deletions(-)

-- 
2.50.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v4 1/3] device property: fix infinite loop in fwnode_for_each_child_node()
  2026-06-11 20:31 [PATCH v4 0/3] device property: fix child iteration issues with secondary fwnodes Andy Shevchenko
@ 2026-06-11 20:31 ` Andy Shevchenko
  2026-06-12  6:47   ` Xu Yang
  2026-06-11 20:31 ` [PATCH v4 2/3] device property: Refactor to use RAII approach Andy Shevchenko
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 9+ messages in thread
From: Andy Shevchenko @ 2026-06-11 20:31 UTC (permalink / raw)
  To: Andy Shevchenko, Xu Yang, linux-acpi, driver-core, linux-kernel
  Cc: Daniel Scally, Heikki Krogerus, Sakari Ailus, Greg Kroah-Hartman,
	Rafael J. Wysocki, Danilo Krummrich, stable

From: Xu Yang <xu.yang_2@nxp.com>

When iterate over children of a fwnode that has a secondary fwnode,
fwnode_get_next_child_node() can enter an infinite loop if the secondary
fwnode has more than one child.

                       Parent        Child
      (Primary fwnode)   FWa:   {FWa1, FWa2, FWa3}
    (Secondary fwnode)   FWb:   {FWb1, FWb2}

In this case:

 ┌─> fwnode_get_next_child_node(FWa, FWa1)
 │    - fwnode_call_ptr_op(FWa, get_next_child_node, FWa1) returns FWa2
 │
 │   ...
 │
 │   fwnode_get_next_child_node(FWa, FWa3)
 │    - fwnode_call_ptr_op(FWa, get_next_child_node, FWa3) returns NULL
 │    - fwnode_call_ptr_op(FWb, get_next_child_node, FWa3) returns FWb1
 │
 │   fwnode_get_next_child_node(FWa, FWb1)
 │    - fwnode_call_ptr_op(FWa, get_next_child_node, FWb1) returns FWa1
 └────┘

This cause fwnode_for_each_child_node() to loop indefinitely, reapeatedly
output {FWa1, FWa2, FWa3, FWb1, FWa1, ...}.

The root cause is that when the current child (FWb1) belongs to the
secondary fwnode, calling get_next_child_node() on the parimary fwnode
incorrectly returns the first child (FWa1) again instead of NULL.

Fix this by dynamically checking the parent fwnode of the current child
before calling get_next_child_node(). This approach follows the pattern
established in commit b5b41ab6b0c1 ("device property: Check
fwnode->secondary in fwnode_graph_get_next_endpoint()").

Fixes: 2692c614f8f0 ("device property: Allow secondary lookup in fwnode_get_next_child_node()")
Cc: stable@vger.kernel.org
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
Tested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 drivers/base/property.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/drivers/base/property.c b/drivers/base/property.c
index 8e0148a37fff..f7b30d9c8716 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -807,18 +807,31 @@ struct fwnode_handle *
 fwnode_get_next_child_node(const struct fwnode_handle *fwnode,
 			   struct fwnode_handle *child)
 {
+	const struct fwnode_handle *parent;
+	struct fwnode_handle *child_parent __free(fwnode_handle) = NULL;
 	struct fwnode_handle *next;
 
-	if (IS_ERR_OR_NULL(fwnode))
+	/*
+	 * If this function is in a loop and the previous iteration returned
+	 * an child from fwnode->secondary, then we need to use the secondary
+	 * as parent rather than @fwnode.
+	 */
+	if (child) {
+		child_parent = fwnode_get_parent(child);
+		parent = child_parent;
+	} else {
+		parent = fwnode;
+	}
+	if (IS_ERR_OR_NULL(parent))
 		return NULL;
 
 	/* Try to find a child in primary fwnode */
-	next = fwnode_call_ptr_op(fwnode, get_next_child_node, child);
+	next = fwnode_call_ptr_op(parent, get_next_child_node, child);
 	if (next)
 		return next;
 
 	/* When no more children in primary, continue with secondary */
-	return fwnode_call_ptr_op(fwnode->secondary, get_next_child_node, child);
+	return fwnode_get_next_child_node(parent->secondary, NULL);
 }
 EXPORT_SYMBOL_GPL(fwnode_get_next_child_node);
 
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v4 2/3] device property: Refactor to use RAII approach
  2026-06-11 20:31 [PATCH v4 0/3] device property: fix child iteration issues with secondary fwnodes Andy Shevchenko
  2026-06-11 20:31 ` [PATCH v4 1/3] device property: fix infinite loop in fwnode_for_each_child_node() Andy Shevchenko
@ 2026-06-11 20:31 ` Andy Shevchenko
  2026-06-11 20:46   ` Rafael J. Wysocki
  2026-06-11 20:31 ` [PATCH v4 3/3] device property: add test cases for fwnode_for_each_child_node() Andy Shevchenko
  2026-07-06  3:16 ` [PATCH v4 0/3] device property: fix child iteration issues with secondary fwnodes Xu Yang
  3 siblings, 1 reply; 9+ messages in thread
From: Andy Shevchenko @ 2026-06-11 20:31 UTC (permalink / raw)
  To: Andy Shevchenko, Xu Yang, linux-acpi, driver-core, linux-kernel
  Cc: Daniel Scally, Heikki Krogerus, Sakari Ailus, Greg Kroah-Hartman,
	Rafael J. Wysocki, Danilo Krummrich

In a couple of functions code can be made cleaner with help of
__free() macro. Refactor these to use RAII approach.

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 drivers/base/property.c | 22 ++++++++--------------
 1 file changed, 8 insertions(+), 14 deletions(-)

diff --git a/drivers/base/property.c b/drivers/base/property.c
index f7b30d9c8716..808e8a90c125 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -7,10 +7,10 @@
  *          Mika Westerberg <mika.westerberg@linux.intel.com>
  */
 
+#include <linux/cleanup.h>
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/export.h>
-#include <linux/kconfig.h>
 #include <linux/of.h>
 #include <linux/property.h>
 #include <linux/phy.h>
@@ -517,7 +517,6 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_string);
 int fwnode_property_match_string(const struct fwnode_handle *fwnode,
 	const char *propname, const char *string)
 {
-	const char **values;
 	int nval, ret;
 
 	nval = fwnode_property_string_array_count(fwnode, propname);
@@ -527,20 +526,18 @@ int fwnode_property_match_string(const struct fwnode_handle *fwnode,
 	if (nval == 0)
 		return -ENODATA;
 
-	values = kcalloc(nval, sizeof(*values), GFP_KERNEL);
+	const char **values __free(kfree) = kcalloc(nval, sizeof(*values), GFP_KERNEL);
 	if (!values)
 		return -ENOMEM;
 
 	ret = fwnode_property_read_string_array(fwnode, propname, values, nval);
 	if (ret < 0)
-		goto out_free;
+		return ret;
 
 	ret = match_string(values, nval, string);
 	if (ret < 0)
-		ret = -ENODATA;
+		return -ENODATA;
 
-out_free:
-	kfree(values);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(fwnode_property_match_string);
@@ -1128,8 +1125,9 @@ struct fwnode_handle *
 fwnode_graph_get_next_endpoint(const struct fwnode_handle *fwnode,
 			       struct fwnode_handle *prev)
 {
-	struct fwnode_handle *ep, *port_parent = NULL;
 	const struct fwnode_handle *parent;
+	struct fwnode_handle *port_parent __free(fwnode_handle) = NULL;
+	struct fwnode_handle *ep;
 
 	/*
 	 * If this function is in a loop and the previous iteration returned
@@ -1147,13 +1145,9 @@ fwnode_graph_get_next_endpoint(const struct fwnode_handle *fwnode,
 
 	ep = fwnode_call_ptr_op(parent, graph_get_next_endpoint, prev);
 	if (ep)
-		goto out_put_port_parent;
+		return ep;
 
-	ep = fwnode_graph_get_next_endpoint(parent->secondary, NULL);
-
-out_put_port_parent:
-	fwnode_handle_put(port_parent);
-	return ep;
+	return fwnode_graph_get_next_endpoint(parent->secondary, NULL);
 }
 EXPORT_SYMBOL_GPL(fwnode_graph_get_next_endpoint);
 
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v4 3/3] device property: add test cases for fwnode_for_each_child_node()
  2026-06-11 20:31 [PATCH v4 0/3] device property: fix child iteration issues with secondary fwnodes Andy Shevchenko
  2026-06-11 20:31 ` [PATCH v4 1/3] device property: fix infinite loop in fwnode_for_each_child_node() Andy Shevchenko
  2026-06-11 20:31 ` [PATCH v4 2/3] device property: Refactor to use RAII approach Andy Shevchenko
@ 2026-06-11 20:31 ` Andy Shevchenko
  2026-07-18 14:18   ` Danilo Krummrich
  2026-07-06  3:16 ` [PATCH v4 0/3] device property: fix child iteration issues with secondary fwnodes Xu Yang
  3 siblings, 1 reply; 9+ messages in thread
From: Andy Shevchenko @ 2026-06-11 20:31 UTC (permalink / raw)
  To: Andy Shevchenko, Xu Yang, linux-acpi, driver-core, linux-kernel
  Cc: Daniel Scally, Heikki Krogerus, Sakari Ailus, Greg Kroah-Hartman,
	Rafael J. Wysocki, Danilo Krummrich

From: Xu Yang <xu.yang_2@nxp.com>

Add test cases for fwnode_for_each_child_node() API.

Test command:
$ ./tools/testing/kunit/kunit.py run property-entry

Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 drivers/base/test/Kconfig               |   1 +
 drivers/base/test/property-entry-test.c | 136 ++++++++++++++++++++++++
 2 files changed, 137 insertions(+)

diff --git a/drivers/base/test/Kconfig b/drivers/base/test/Kconfig
index 2756870615cc..95fc42e91564 100644
--- a/drivers/base/test/Kconfig
+++ b/drivers/base/test/Kconfig
@@ -17,4 +17,5 @@ config DM_KUNIT_TEST
 config DRIVER_PE_KUNIT_TEST
 	tristate "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS
 	depends on KUNIT
+	select OF
 	default KUNIT_ALL_TESTS
diff --git a/drivers/base/test/property-entry-test.c b/drivers/base/test/property-entry-test.c
index a8657eb06f94..a3d1caf28cc7 100644
--- a/drivers/base/test/property-entry-test.c
+++ b/drivers/base/test/property-entry-test.c
@@ -4,6 +4,8 @@
 // Copyright 2019 Google LLC.
 
 #include <kunit/test.h>
+
+#include <linux/of.h>
 #include <linux/property.h>
 #include <linux/types.h>
 
@@ -489,6 +491,139 @@ static void pe_test_reference(struct kunit *test)
 	software_node_unregister_node_group(group);
 }
 
+static struct fwnode_handle *create_device_node(struct kunit *test,
+						const char *name,
+						const char *full_name,
+						struct device_node *parent)
+{
+	struct device_node *node;
+
+	node = kunit_kzalloc(test, sizeof(*node), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
+
+	node->name = kunit_kstrdup(test, name, GFP_KERNEL);
+	node->full_name = kunit_kstrdup(test, full_name, GFP_KERNEL);
+
+	if (parent) {
+		node->sibling = parent->child;
+		/* set the node as the first child of the parent */
+		parent->child = node;
+		node->parent = parent;
+	}
+
+	of_node_init(node);
+	return of_fwnode_handle(node);
+}
+
+/* Verifies that fwnode_for_each_child_node() can output correct children */
+static void pe_test_child_iteration(struct kunit *test)
+{
+	struct fwnode_handle *of_node, *of_node1;
+	struct fwnode_handle *sw_node, *sw_node1;
+	struct fwnode_handle *child;
+	int error, i, num;
+
+	static const struct software_node node = { .name = "sw" };
+	static const struct software_node node1 = { .name = "sw-1", .parent = &node};
+	static const struct software_node node2 = { .name = "sw-2", .parent = &node};
+	static const struct software_node node3 = { .name = "sw-3", .parent = &node};
+	static const struct software_node *group[] = { &node, &node1, &node2, &node3, NULL };
+
+	static const char * const of_child_array[] = { "of-1", "of-2", "of-3" };
+	static const char * const sw_child_array[] = { "sw-1", "sw-2", "sw-3" };
+	static const char * const of_sw_child_array[] = { "of-1", "of-2", "of-3",
+							  "sw-1", "sw-2", "sw-3" };
+	static const char * const sw_of_child_array[] = { "sw-1", "sw-2", "sw-3",
+							  "of-1", "of-2", "of-3" };
+
+	/* 1. Test OF node child iteration */
+
+	of_node = create_device_node(test, "of", "of", NULL);
+	create_device_node(test, "of", "of-3", to_of_node(of_node));
+	create_device_node(test, "of", "of-2", to_of_node(of_node));
+	of_node1 = create_device_node(test, "of", "of-1", to_of_node(of_node));
+
+	i = 0;
+	num = ARRAY_SIZE(of_child_array);
+	fwnode_for_each_child_node(of_node, child) {
+		KUNIT_ASSERT_LT(test, i, num);
+		KUNIT_EXPECT_STREQ(test, of_child_array[i++], fwnode_get_name(child));
+	}
+	KUNIT_EXPECT_PTR_EQ(test, child, NULL);
+
+	/* 2. Test SW node child iteration */
+
+	error = software_node_register_node_group(group);
+	KUNIT_ASSERT_EQ(test, error, 0);
+
+	sw_node = software_node_fwnode(&node);
+
+	i = 0;
+	num = ARRAY_SIZE(sw_child_array);
+	fwnode_for_each_child_node(sw_node, child) {
+		KUNIT_ASSERT_LT(test, i, num);
+		KUNIT_EXPECT_STREQ(test, sw_child_array[i++], fwnode_get_name(child));
+	}
+	KUNIT_EXPECT_PTR_EQ(test, child, NULL);
+
+	/* 3. Test OF (primary) + SW (secondary) node child iteration */
+
+	of_node->secondary = sw_node;
+	sw_node->secondary = ERR_PTR(-ENODEV);
+
+	i = 0;
+	num = ARRAY_SIZE(of_sw_child_array);
+	fwnode_for_each_child_node(of_node, child) {
+		KUNIT_ASSERT_LT(test, i, num);
+		KUNIT_EXPECT_STREQ(test, of_sw_child_array[i++], fwnode_get_name(child));
+	}
+	KUNIT_EXPECT_PTR_EQ(test, child, NULL);
+
+	/* 4. Test SW (primary) + OF (secondary) node child iteration */
+
+	sw_node->secondary = of_node;
+	of_node->secondary = ERR_PTR(-ENODEV);
+
+	i = 0;
+	num = ARRAY_SIZE(sw_of_child_array);
+	fwnode_for_each_child_node(sw_node, child) {
+		KUNIT_ASSERT_LT(test, i, num);
+		KUNIT_EXPECT_STREQ(test, sw_of_child_array[i++], fwnode_get_name(child));
+	}
+	KUNIT_EXPECT_PTR_EQ(test, child, NULL);
+
+	/* 5. Test OF (primary) + SW (secondary, but no children) node child iteration */
+
+	sw_node1 = software_node_fwnode(&node1);
+	of_node->secondary = sw_node1;
+	sw_node->secondary = ERR_PTR(-ENODEV);
+
+	i = 0;
+	num = ARRAY_SIZE(of_child_array);
+	fwnode_for_each_child_node(of_node, child) {
+		KUNIT_ASSERT_LT(test, i, num);
+		KUNIT_EXPECT_STREQ(test, of_child_array[i++], fwnode_get_name(child));
+	}
+	KUNIT_EXPECT_PTR_EQ(test, child, NULL);
+
+	/* 6. Test SW (primary) + OF (secondary, but no children) node child iteration */
+
+	sw_node->secondary = of_node1;
+	of_node->secondary = ERR_PTR(-ENODEV);
+
+	i = 0;
+	num = ARRAY_SIZE(sw_child_array);
+	fwnode_for_each_child_node(sw_node, child) {
+		KUNIT_ASSERT_LT(test, i, num);
+		KUNIT_EXPECT_STREQ(test, sw_child_array[i++], fwnode_get_name(child));
+	}
+	KUNIT_EXPECT_PTR_EQ(test, child, NULL);
+
+	of_node->secondary = NULL;
+	sw_node->secondary = NULL;
+	software_node_unregister_node_group(group);
+}
+
 static struct kunit_case property_entry_test_cases[] = {
 	KUNIT_CASE(pe_test_uints),
 	KUNIT_CASE(pe_test_uint_arrays),
@@ -497,6 +632,7 @@ static struct kunit_case property_entry_test_cases[] = {
 	KUNIT_CASE(pe_test_move_inline_u8),
 	KUNIT_CASE(pe_test_move_inline_str),
 	KUNIT_CASE(pe_test_reference),
+	KUNIT_CASE(pe_test_child_iteration),
 	{ }
 };
 
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 2/3] device property: Refactor to use RAII approach
  2026-06-11 20:31 ` [PATCH v4 2/3] device property: Refactor to use RAII approach Andy Shevchenko
@ 2026-06-11 20:46   ` Rafael J. Wysocki
  2026-06-11 21:12     ` Andy Shevchenko
  0 siblings, 1 reply; 9+ messages in thread
From: Rafael J. Wysocki @ 2026-06-11 20:46 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Xu Yang, linux-acpi, driver-core, linux-kernel, Daniel Scally,
	Heikki Krogerus, Sakari Ailus, Greg Kroah-Hartman,
	Rafael J. Wysocki, Danilo Krummrich

On Thu, Jun 11, 2026 at 10:35 PM Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
>
> In a couple of functions code can be made cleaner with help of
> __free() macro. Refactor these to use RAII approach.
>
> Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> ---
>  drivers/base/property.c | 22 ++++++++--------------
>  1 file changed, 8 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/base/property.c b/drivers/base/property.c
> index f7b30d9c8716..808e8a90c125 100644
> --- a/drivers/base/property.c
> +++ b/drivers/base/property.c
> @@ -7,10 +7,10 @@
>   *          Mika Westerberg <mika.westerberg@linux.intel.com>
>   */
>
> +#include <linux/cleanup.h>
>  #include <linux/device.h>
>  #include <linux/err.h>
>  #include <linux/export.h>
> -#include <linux/kconfig.h>
>  #include <linux/of.h>
>  #include <linux/property.h>
>  #include <linux/phy.h>
> @@ -517,7 +517,6 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_string);
>  int fwnode_property_match_string(const struct fwnode_handle *fwnode,
>         const char *propname, const char *string)
>  {
> -       const char **values;
>         int nval, ret;
>
>         nval = fwnode_property_string_array_count(fwnode, propname);
> @@ -527,20 +526,18 @@ int fwnode_property_match_string(const struct fwnode_handle *fwnode,
>         if (nval == 0)
>                 return -ENODATA;
>
> -       values = kcalloc(nval, sizeof(*values), GFP_KERNEL);
> +       const char **values __free(kfree) = kcalloc(nval, sizeof(*values), GFP_KERNEL);
>         if (!values)
>                 return -ENOMEM;
>
>         ret = fwnode_property_read_string_array(fwnode, propname, values, nval);
>         if (ret < 0)
> -               goto out_free;
> +               return ret;
>
>         ret = match_string(values, nval, string);
>         if (ret < 0)
> -               ret = -ENODATA;
> +               return -ENODATA;
>
> -out_free:
> -       kfree(values);
>         return ret;
>  }
>  EXPORT_SYMBOL_GPL(fwnode_property_match_string);
> @@ -1128,8 +1125,9 @@ struct fwnode_handle *
>  fwnode_graph_get_next_endpoint(const struct fwnode_handle *fwnode,
>                                struct fwnode_handle *prev)
>  {
> -       struct fwnode_handle *ep, *port_parent = NULL;
>         const struct fwnode_handle *parent;
> +       struct fwnode_handle *port_parent __free(fwnode_handle) = NULL;

The thing on the right-hand side of the assignment should be a
constructor, shouldn't it?

> +       struct fwnode_handle *ep;
>
>         /*
>          * If this function is in a loop and the previous iteration returned
> @@ -1147,13 +1145,9 @@ fwnode_graph_get_next_endpoint(const struct fwnode_handle *fwnode,
>
>         ep = fwnode_call_ptr_op(parent, graph_get_next_endpoint, prev);
>         if (ep)
> -               goto out_put_port_parent;
> +               return ep;
>
> -       ep = fwnode_graph_get_next_endpoint(parent->secondary, NULL);
> -
> -out_put_port_parent:
> -       fwnode_handle_put(port_parent);
> -       return ep;
> +       return fwnode_graph_get_next_endpoint(parent->secondary, NULL);
>  }
>  EXPORT_SYMBOL_GPL(fwnode_graph_get_next_endpoint);
>
> --
> 2.50.1
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 2/3] device property: Refactor to use RAII approach
  2026-06-11 20:46   ` Rafael J. Wysocki
@ 2026-06-11 21:12     ` Andy Shevchenko
  0 siblings, 0 replies; 9+ messages in thread
From: Andy Shevchenko @ 2026-06-11 21:12 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Xu Yang, linux-acpi, driver-core, linux-kernel, Daniel Scally,
	Heikki Krogerus, Sakari Ailus, Greg Kroah-Hartman,
	Danilo Krummrich

On Thu, Jun 11, 2026 at 10:46:31PM +0200, Rafael J. Wysocki wrote:
> On Thu, Jun 11, 2026 at 10:35 PM Andy Shevchenko
> <andriy.shevchenko@linux.intel.com> wrote:
> > In a couple of functions code can be made cleaner with help of
> > __free() macro. Refactor these to use RAII approach.

...

> >  fwnode_graph_get_next_endpoint(const struct fwnode_handle *fwnode,
> >                                struct fwnode_handle *prev)
> >  {
> > -       struct fwnode_handle *ep, *port_parent = NULL;
> >         const struct fwnode_handle *parent;
> > +       struct fwnode_handle *port_parent __free(fwnode_handle) = NULL;
> 
> The thing on the right-hand side of the assignment should be a
> constructor, shouldn't it?

Nope. It's a recommendation to do a such, but in this case it's hard to achieve
(since the constructor is inside the conditional scope) and actually is not needed
as we don't mix up with other RAII calls, such a guard()().

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 1/3] device property: fix infinite loop in fwnode_for_each_child_node()
  2026-06-11 20:31 ` [PATCH v4 1/3] device property: fix infinite loop in fwnode_for_each_child_node() Andy Shevchenko
@ 2026-06-12  6:47   ` Xu Yang
  0 siblings, 0 replies; 9+ messages in thread
From: Xu Yang @ 2026-06-12  6:47 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Xu Yang, linux-acpi, driver-core, linux-kernel, Daniel Scally,
	Heikki Krogerus, Sakari Ailus, Greg Kroah-Hartman,
	Rafael J. Wysocki, Danilo Krummrich, stable

On Thu, Jun 11, 2026 at 10:31:06PM +0200, Andy Shevchenko wrote:
> From: Xu Yang <xu.yang_2@nxp.com>
> 
> When iterate over children of a fwnode that has a secondary fwnode,
> fwnode_get_next_child_node() can enter an infinite loop if the secondary
> fwnode has more than one child.
> 
>                        Parent        Child
>       (Primary fwnode)   FWa:   {FWa1, FWa2, FWa3}
>     (Secondary fwnode)   FWb:   {FWb1, FWb2}
> 
> In this case:
> 
>  ┌─> fwnode_get_next_child_node(FWa, FWa1)
>  │    - fwnode_call_ptr_op(FWa, get_next_child_node, FWa1) returns FWa2
>  │
>  │   ...
>  │
>  │   fwnode_get_next_child_node(FWa, FWa3)
>  │    - fwnode_call_ptr_op(FWa, get_next_child_node, FWa3) returns NULL
>  │    - fwnode_call_ptr_op(FWb, get_next_child_node, FWa3) returns FWb1
>  │
>  │   fwnode_get_next_child_node(FWa, FWb1)
>  │    - fwnode_call_ptr_op(FWa, get_next_child_node, FWb1) returns FWa1
>  └────┘
> 
> This cause fwnode_for_each_child_node() to loop indefinitely, reapeatedly
> output {FWa1, FWa2, FWa3, FWb1, FWa1, ...}.
> 
> The root cause is that when the current child (FWb1) belongs to the
> secondary fwnode, calling get_next_child_node() on the parimary fwnode
> incorrectly returns the first child (FWa1) again instead of NULL.
> 
> Fix this by dynamically checking the parent fwnode of the current child
> before calling get_next_child_node(). This approach follows the pattern
> established in commit b5b41ab6b0c1 ("device property: Check
> fwnode->secondary in fwnode_graph_get_next_endpoint()").
> 
> Fixes: 2692c614f8f0 ("device property: Allow secondary lookup in fwnode_get_next_child_node()")
> Cc: stable@vger.kernel.org
> Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
> Tested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>

Tested-by: Xu Yang <xu.yang_2@nxp.com>

Thanks,
Xu Yang

> ---
>  drivers/base/property.c | 19 ++++++++++++++++---
>  1 file changed, 16 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/base/property.c b/drivers/base/property.c
> index 8e0148a37fff..f7b30d9c8716 100644
> --- a/drivers/base/property.c
> +++ b/drivers/base/property.c
> @@ -807,18 +807,31 @@ struct fwnode_handle *
>  fwnode_get_next_child_node(const struct fwnode_handle *fwnode,
>  			   struct fwnode_handle *child)
>  {
> +	const struct fwnode_handle *parent;
> +	struct fwnode_handle *child_parent __free(fwnode_handle) = NULL;
>  	struct fwnode_handle *next;
>  
> -	if (IS_ERR_OR_NULL(fwnode))
> +	/*
> +	 * If this function is in a loop and the previous iteration returned
> +	 * an child from fwnode->secondary, then we need to use the secondary
> +	 * as parent rather than @fwnode.
> +	 */
> +	if (child) {
> +		child_parent = fwnode_get_parent(child);
> +		parent = child_parent;
> +	} else {
> +		parent = fwnode;
> +	}
> +	if (IS_ERR_OR_NULL(parent))
>  		return NULL;
>  
>  	/* Try to find a child in primary fwnode */
> -	next = fwnode_call_ptr_op(fwnode, get_next_child_node, child);
> +	next = fwnode_call_ptr_op(parent, get_next_child_node, child);
>  	if (next)
>  		return next;
>  
>  	/* When no more children in primary, continue with secondary */
> -	return fwnode_call_ptr_op(fwnode->secondary, get_next_child_node, child);
> +	return fwnode_get_next_child_node(parent->secondary, NULL);
>  }
>  EXPORT_SYMBOL_GPL(fwnode_get_next_child_node);
>  
> -- 
> 2.50.1
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 0/3] device property: fix child iteration issues with secondary fwnodes
  2026-06-11 20:31 [PATCH v4 0/3] device property: fix child iteration issues with secondary fwnodes Andy Shevchenko
                   ` (2 preceding siblings ...)
  2026-06-11 20:31 ` [PATCH v4 3/3] device property: add test cases for fwnode_for_each_child_node() Andy Shevchenko
@ 2026-07-06  3:16 ` Xu Yang
  3 siblings, 0 replies; 9+ messages in thread
From: Xu Yang @ 2026-07-06  3:16 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Danilo Krummrich, Rafael J. Wysocki
  Cc: Andy Shevchenko, Xu Yang, linux-acpi, driver-core, linux-kernel,
	Daniel Scally, Heikki Krogerus, Sakari Ailus

Hi Greg and Danilo,

On Thu, Jun 11, 2026 at 10:31:05PM +0200, Andy Shevchenko wrote:
> This series fixes two issues in the fwnode child iteration logic when
> a secondary fwnode is present.
> 
> The first issue is  a refcount imbalance in software_node_get_next_child().
> When a software node is used as a secondary fwnode, the iteration code may
> incorrectly decrement the refcount of child nodes that do not belong to the
> software node hierarchy. This results in refcount underflow and possible
> use-after-free.
> 
> The second issue is an infinite loop in fwnode_for_each_child_node(), caused
> by improper handling of iteration state across primary and secondary fwnodes.
> When iterating over children from both primary and secondary fwnodes, the code
> may incorrectly resume iteration from the primary fwnode even when the current
> child belongs to the secondary, leading to repeated traversal and a loop.
> 
> Both issues are triggered when mixing different fwnode types through the
> secondary mechanism, and stem from incorrect assumptions about ownership
> and traversal context of child nodes.

Gentle ping on this series.

Thanks,
Xu Yang

> 
> Changes in v4:
> - amended the fix and test case (Andy)
> - added patch 2 to align other implementations with RAII approach (Andy)
> - tested on Intel Galileo board for which the initial code was developed (Andy)
> - Link to v3: https://patch.msgid.link/20260605-fixes_fwnode_iteration-v3-0-44c18472e1d1@nxp.com
> 
> Changes in v3:
> - remove software node patch
> - add a kunit test case suggested by Andy Shevchenko
> - Link to v2: https://patch.msgid.link/20260603-fixes_fwnode_iteration-v2-0-0ae381f8b7b9@nxp.com
> 
> Changes in v2:
> - use __free() to cleanup parent fwnode
> - Link to v1: https://lore.kernel.org/r/20260525-fixes_fwnode_iteration-v1-0-a12903fb2919@nxp.com
> 
> Andy Shevchenko (1):
>   device property: Refactor to use RAII approach
> 
> Xu Yang (2):
>   device property: fix infinite loop in fwnode_for_each_child_node()
>   device property: add test cases for fwnode_for_each_child_node()
> 
>  drivers/base/property.c                 |  41 ++++---
>  drivers/base/test/Kconfig               |   1 +
>  drivers/base/test/property-entry-test.c | 136 ++++++++++++++++++++++++
>  3 files changed, 161 insertions(+), 17 deletions(-)
> 
> -- 
> 2.50.1
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 3/3] device property: add test cases for fwnode_for_each_child_node()
  2026-06-11 20:31 ` [PATCH v4 3/3] device property: add test cases for fwnode_for_each_child_node() Andy Shevchenko
@ 2026-07-18 14:18   ` Danilo Krummrich
  0 siblings, 0 replies; 9+ messages in thread
From: Danilo Krummrich @ 2026-07-18 14:18 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Xu Yang, linux-acpi, driver-core, linux-kernel, Daniel Scally,
	Heikki Krogerus, Sakari Ailus, Greg Kroah-Hartman,
	Rafael J. Wysocki, Rob Herring, Saravana Kannan, devicetree

(Cc: OF)

On Thu Jun 11, 2026 at 10:31 PM CEST, Andy Shevchenko wrote:
> From: Xu Yang <xu.yang_2@nxp.com>
>
> Add test cases for fwnode_for_each_child_node() API.
>
> Test command:
> $ ./tools/testing/kunit/kunit.py run property-entry
>
> Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
> Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>

This patch broke the build [1], hence I dropped it from the queue (but kept the
first two patches of this series).

of_node_init() can't be used from modules, either it needs EXPORT_SYMBOL_GPL()
itself, or of_node_ktype needs to be exported for this kunit test to work.

[1] https://lore.kernel.org/all/202607181651.RnUuV8n6-lkp@intel.com/

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-07-18 14:18 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-11 20:31 [PATCH v4 0/3] device property: fix child iteration issues with secondary fwnodes Andy Shevchenko
2026-06-11 20:31 ` [PATCH v4 1/3] device property: fix infinite loop in fwnode_for_each_child_node() Andy Shevchenko
2026-06-12  6:47   ` Xu Yang
2026-06-11 20:31 ` [PATCH v4 2/3] device property: Refactor to use RAII approach Andy Shevchenko
2026-06-11 20:46   ` Rafael J. Wysocki
2026-06-11 21:12     ` Andy Shevchenko
2026-06-11 20:31 ` [PATCH v4 3/3] device property: add test cases for fwnode_for_each_child_node() Andy Shevchenko
2026-07-18 14:18   ` Danilo Krummrich
2026-07-06  3:16 ` [PATCH v4 0/3] device property: fix child iteration issues with secondary fwnodes Xu Yang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox