[PATCH v4 1/6] of: resolve alias-prefixed paths under devtree_lock

From: Abdurrahman Hussain

Date: Tue Jul 21 2026 - 22:50:18 EST


of_find_node_opts_by_path() resolves an alias-prefixed path by
walking the property list of the of_aliases node with no lock held
and no reference taken on the node. Property surgery on /aliases —
of_add_property(), of_remove_property(), changeset apply/revert —
mutates that list under devtree_lock, so the lockless walk can step
into a property that is being unlinked. Nothing keeps the node itself
alive across the walk either: detaching /aliases and dropping the
last reference frees the node and its property list mid-iteration.

The race has existed since the walk was introduced, but is hard to
hit with a boot-FDT /aliases node that nothing ever detaches. The
rest of this series makes /aliases dynamic — overlays can add and
remove properties, and create and destroy the node itself — so close
it first: find the matching property under devtree_lock with a
reference held on of_aliases, and keep that reference across the
of_find_node_by_path() call that resolves the value. The reference is
what keeps the value string valid outside the lock: a concurrently
removed property moves to the node's deadprops list and is only freed
when the node itself is released.

While here, validate the value's shape before handing it to
of_find_node_by_path(): /aliases contents can now come from overlays
and from raw changeset/of_add_property() callers, and only the
overlay path validates at the producer. of_alias_value_ok() (shared
with the alias tracking added later in this series) rejects values
that are not non-empty C strings NUL-terminated within the property
length, so the consumer no longer trusts any producer. This also
covers the empty property (NULL value) that previously crashed in
strchr().

The name match is folded into one bounded strncmp plus a check of the
terminating NUL instead of strlen + strncmp, halving the string
traversal now done with interrupts disabled.

Assisted-by: Claude:claude-fable-5 [Claude Code]
Signed-off-by: Abdurrahman Hussain <abdurrahman@xxxxxxxxxx>
---
drivers/of/base.c | 26 ++++++++++++++++++--------
drivers/of/of_private.h | 7 +++++++
2 files changed, 25 insertions(+), 8 deletions(-)

diff --git a/drivers/of/base.c b/drivers/of/base.c
index 6e7a42dedad3..9c2770823889 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -995,6 +995,8 @@ struct device_node *of_find_node_opts_by_path(const char *path, const char **opt

/* The path could begin with an alias */
if (*path != '/') {
+ struct device_node *aliases;
+ const char *value = NULL;
int len;
const char *p = strchrnul(path, '/');

@@ -1002,16 +1004,24 @@ struct device_node *of_find_node_opts_by_path(const char *path, const char **opt
p = separator;
len = p - path;

- /* of_aliases must not be NULL */
- if (!of_aliases)
- return NULL;
-
- for_each_property_of_node(of_aliases, pp) {
- if (strlen(pp->name) == len && !strncmp(pp->name, path, len)) {
- np = of_find_node_by_path(pp->value);
- break;
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+ aliases = of_node_get(of_aliases);
+ if (aliases) {
+ for_each_property_of_node(aliases, pp) {
+ if (!strncmp(pp->name, path, len) &&
+ !pp->name[len]) {
+ if (of_alias_value_ok(pp))
+ value = pp->value;
+ break;
+ }
}
}
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+
+ /* the reference on @aliases keeps @value alive */
+ if (value)
+ np = of_find_node_by_path(value);
+ of_node_put(aliases);
if (!np)
return NULL;
path = p;
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index 0ae16da066e2..792756389691 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -215,6 +215,13 @@ static inline bool is_pseudo_property(const char *prop_name)
!of_prop_cmp(prop_name, "linux,phandle");
}

+/* alias values are deref'd as C strings; they must terminate within length */
+static inline bool of_alias_value_ok(const struct property *pp)
+{
+ return pp->value && pp->length >= 2 &&
+ strnlen(pp->value, pp->length) < pp->length;
+}
+
#if IS_ENABLED(CONFIG_KUNIT)
int __of_address_resource_bounds(struct resource *r, u64 start, u64 size);
#endif

--
2.54.0