On Sat, Jan 4, 2025 at 3:06 PM Stephen Gordon <gordoste@xxxxxxxxxxxx> wrote: > > This new function returns nodes in address order. There is also a > corresponding for_each_child_of_node_by_id() macro. > > Signed-off-by: Stephen Gordon <gordoste@xxxxxxxxxxxx> > --- > drivers/of/base.c | 41 ++++++++++++++++++++++++++++++++++++++++- > include/linux/of.h | 12 ++++++++++++ > 2 files changed, 52 insertions(+), 1 deletion(-) > > diff --git a/drivers/of/base.c b/drivers/of/base.c > index 7dc394255a0a..0be430b022fb 100644 > --- a/drivers/of/base.c > +++ b/drivers/of/base.c > @@ -623,7 +623,46 @@ static struct device_node *__of_get_next_child(const struct device_node *node, > child = __of_get_next_child(parent, child)) > > /** > - * of_get_next_child - Iterate a node childs > + * of_get_next_child_by_id - Iterate a node's children in order of id > + * @node: parent node > + * @prev: previous child of the parent node, or NULL to get first > + * > + * Return: A node pointer with refcount incremented, use of_node_put() on > + * it when done. Returns NULL when prev is the last child. Decrements the > + * refcount of prev. > + */ > +struct device_node *of_get_next_child_by_id(const struct device_node *node, > + struct device_node *prev) > +{ > + struct device_node *next = NULL; > + u32 next_id = U32_MAX; > + u32 prev_id, this_id; > + unsigned long flags; > + > + if (!node || !(node->child)) > + return NULL; > + > + if (prev) > + of_property_read_u32(prev, "reg", &prev_id); This should use of_property_read_reg() so that it works for different address sizes. That however creates a dependency on CONFIG_OF_ADDRESS. It's probably fine if this function doesn't work for !CONFIG_OF_ADDRESS. That's just Sparc and Sparc platforms don't use OF graph. > + > + for_each_child_of_node_scoped(node, child) { > + of_property_read_u32(child, "reg", &this_id); > + if ((!prev || (this_id > prev_id)) && this_id < next_id) { > + next = child; > + next_id = this_id; > + } > + } The loop just did a put on next/child, so it can be freed here before you take the lock and you have a UAF. You need to use return_ptr() since it outlives the loop. > + > + raw_spin_lock_irqsave(&devtree_lock, flags); And this lock is not needed. > + of_node_get(next); Nor this get since the loop did a get already. > + of_node_put(prev); > + raw_spin_unlock_irqrestore(&devtree_lock, flags); > + return next; > +} > +EXPORT_SYMBOL(of_get_next_child_by_id); Rob