* [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
* 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
* [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
* 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
* [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 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
* 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
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