* [PATCH 1/2] drivers/of: validate live-tree string properties before string use
@ 2026-04-03 7:32 Pengpeng Hou
2026-04-03 7:33 ` [PATCH 2/2] drivers/of: validate status properties in reconfig state changes Pengpeng Hou
` (3 more replies)
0 siblings, 4 replies; 11+ messages in thread
From: Pengpeng Hou @ 2026-04-03 7:32 UTC (permalink / raw)
To: Rob Herring, Saravana Kannan; +Cc: devicetree, linux-kernel, pengpeng
`populate_properties()` stores live-tree property values as raw byte
sequences plus a separate `length`. They are not globally guaranteed to
be NUL-terminated.
`of_prop_next_string()` currently advances through string-list
properties with `strlen()`, `__of_node_is_type()` compares raw
`device_type` bytes with `strcmp()`, `__of_device_is_status()` compares
raw `status` bytes with `strcmp()`/`strncmp()`, and
`of_alias_from_compatible()` treats the first `compatible` entry as a
NUL-terminated string.
Validate these strings within their property bounds before treating
them as C strings. In particular, reject malformed string-list entries
whose next string is not terminated before `of_prop_next_string()`
returns it.
Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
drivers/of/base.c | 39 +++++++++++++++++++++++----------------
drivers/of/property.c | 30 +++++++++++++++++++++++++-----
2 files changed, 48 insertions(+), 21 deletions(-)
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 57420806c1a2..3c6af4051ad3 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -82,7 +82,14 @@ EXPORT_SYMBOL(of_node_name_prefix);
static bool __of_node_is_type(const struct device_node *np, const char *type)
{
- const char *match = __of_get_property(np, "device_type", NULL);
+ const char *match;
+ int matchlen;
+
+ match = __of_get_property(np, "device_type", &matchlen);
+ if (!match || matchlen <= 0)
+ return false;
+ if (strnlen(match, matchlen) >= matchlen)
+ return false;
return np && match && type && !strcmp(match, type);
}
@@ -491,22 +498,22 @@ static bool __of_device_is_status(const struct device_node *device,
return false;
status = __of_get_property(device, "status", &statlen);
- if (status == NULL)
+ if (!status || statlen <= 0)
+ return false;
+ if (strnlen(status, statlen) >= statlen)
return false;
- if (statlen > 0) {
- while (*strings) {
- unsigned int len = strlen(*strings);
+ while (*strings) {
+ unsigned int len = strlen(*strings);
- if ((*strings)[len - 1] == '-') {
- if (!strncmp(status, *strings, len))
- return true;
- } else {
- if (!strcmp(status, *strings))
- return true;
- }
- strings++;
+ if ((*strings)[len - 1] == '-') {
+ if (!strncmp(status, *strings, len))
+ return true;
+ } else {
+ if (!strcmp(status, *strings))
+ return true;
}
+ strings++;
}
return false;
@@ -1217,10 +1224,10 @@ EXPORT_SYMBOL(of_find_matching_node_and_match);
int of_alias_from_compatible(const struct device_node *node, char *alias, int len)
{
const char *compatible, *p;
- int cplen;
+ int ret;
- compatible = of_get_property(node, "compatible", &cplen);
- if (!compatible || strlen(compatible) > cplen)
+ ret = of_property_read_string_index(node, "compatible", 0, &compatible);
+ if (ret)
return -ENODEV;
p = strchr(compatible, ',');
strscpy(alias, p ? p + 1 : compatible, len);
diff --git a/drivers/of/property.c b/drivers/of/property.c
index 50d95d512bf5..edbc7a95aa4c 100644
--- a/drivers/of/property.c
+++ b/drivers/of/property.c
@@ -648,16 +648,36 @@ EXPORT_SYMBOL_GPL(of_prop_next_u32);
const char *of_prop_next_string(const struct property *prop, const char *cur)
{
- const void *curv = cur;
+ const char *curv = cur;
+ const char *end;
+ size_t len;
- if (!prop)
+ if (!prop || !prop->value || !prop->length)
return NULL;
- if (!cur)
+ end = prop->value + prop->length;
+
+ if (!cur) {
+ len = strnlen(prop->value, prop->length);
+ if (len >= prop->length)
+ return NULL;
+
return prop->value;
+ }
- curv += strlen(cur) + 1;
- if (curv >= prop->value + prop->length)
+ if (cur < (const char *)prop->value || cur >= end)
+ return NULL;
+
+ len = strnlen(cur, end - cur);
+ if (len >= end - cur)
+ return NULL;
+
+ curv += len + 1;
+ if (curv >= end)
+ return NULL;
+
+ len = strnlen(curv, end - curv);
+ if (len >= end - curv)
return NULL;
return curv;
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH 2/2] drivers/of: validate status properties in reconfig state changes 2026-04-03 7:32 [PATCH 1/2] drivers/of: validate live-tree string properties before string use Pengpeng Hou @ 2026-04-03 7:33 ` Pengpeng Hou 2026-04-13 17:14 ` [PATCH 1/2] drivers/of: validate live-tree string properties before string use Rob Herring ` (2 subsequent siblings) 3 siblings, 0 replies; 11+ messages in thread From: Pengpeng Hou @ 2026-04-03 7:33 UTC (permalink / raw) To: Rob Herring, Saravana Kannan; +Cc: devicetree, linux-kernel, pengpeng Live-tree reconfiguration properties also carry raw values plus explicit lengths. `of_reconfig_get_state_change()` currently treats `status` property values as NUL-terminated strings and feeds them straight into `strcmp()`. Factor the `"okay"` / `"ok"` check out into a helper that first verifies that the property contains a bounded C string within `prop->length`. Malformed `status` updates should be treated as not enabling the node. Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn> --- drivers/of/dynamic.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 1a06175def37..efee59ed371a 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -74,6 +74,20 @@ static const char *action_names[] = { [OF_RECONFIG_UPDATE_PROPERTY] = "UPDATE_PROPERTY", }; +static bool of_property_status_ok(const struct property *prop) +{ + const char *status; + + if (!prop || !prop->value || prop->length <= 0) + return false; + + status = prop->value; + if (strnlen(status, prop->length) >= prop->length) + return false; + + return !strcmp(status, "okay") || !strcmp(status, "ok"); +} + #define _do_print(func, prefix, action, node, prop, ...) ({ \ func("changeset: " prefix "%-15s %pOF%s%s\n", \ ##__VA_ARGS__, action_names[action], node, \ @@ -135,11 +149,9 @@ int of_reconfig_get_state_change(unsigned long action, struct of_reconfig_data * if (prop && !strcmp(prop->name, "status")) { is_status = 1; - status_state = !strcmp(prop->value, "okay") || - !strcmp(prop->value, "ok"); + status_state = of_property_status_ok(prop); if (old_prop) - old_status_state = !strcmp(old_prop->value, "okay") || - !strcmp(old_prop->value, "ok"); + old_status_state = of_property_status_ok(old_prop); } switch (action) { -- 2.50.1 (Apple Git-155) ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 1/2] drivers/of: validate live-tree string properties before string use 2026-04-03 7:32 [PATCH 1/2] drivers/of: validate live-tree string properties before string use Pengpeng Hou 2026-04-03 7:33 ` [PATCH 2/2] drivers/of: validate status properties in reconfig state changes Pengpeng Hou @ 2026-04-13 17:14 ` Rob Herring 2026-04-17 3:06 ` Pengpeng Hou 2026-04-17 12:36 ` [PATCH v2 " Pengpeng Hou 3 siblings, 0 replies; 11+ messages in thread From: Rob Herring @ 2026-04-13 17:14 UTC (permalink / raw) To: Pengpeng Hou; +Cc: Saravana Kannan, devicetree, linux-kernel On Fri, Apr 03, 2026 at 03:32:30PM +0800, Pengpeng Hou wrote: > `populate_properties()` stores live-tree property values as raw byte > sequences plus a separate `length`. They are not globally guaranteed to > be NUL-terminated. > > `of_prop_next_string()` currently advances through string-list > properties with `strlen()`, `__of_node_is_type()` compares raw > `device_type` bytes with `strcmp()`, `__of_device_is_status()` compares > raw `status` bytes with `strcmp()`/`strncmp()`, and > `of_alias_from_compatible()` treats the first `compatible` entry as a > NUL-terminated string. > > Validate these strings within their property bounds before treating > them as C strings. In particular, reject malformed string-list entries > whose next string is not terminated before `of_prop_next_string()` > returns it. > > Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn> > > --- > drivers/of/base.c | 39 +++++++++++++++++++++++---------------- > drivers/of/property.c | 30 +++++++++++++++++++++++++----- > 2 files changed, 48 insertions(+), 21 deletions(-) > > diff --git a/drivers/of/base.c b/drivers/of/base.c > index 57420806c1a2..3c6af4051ad3 100644 > --- a/drivers/of/base.c > +++ b/drivers/of/base.c > @@ -82,7 +82,14 @@ EXPORT_SYMBOL(of_node_name_prefix); > > static bool __of_node_is_type(const struct device_node *np, const char *type) > { > - const char *match = __of_get_property(np, "device_type", NULL); > + const char *match; > + int matchlen; > + > + match = __of_get_property(np, "device_type", &matchlen); > + if (!match || matchlen <= 0) > + return false; > + if (strnlen(match, matchlen) >= matchlen) > + return false; Can't we use of_property_match_string() instead? > > return np && match && type && !strcmp(match, type); > } > @@ -491,22 +498,22 @@ static bool __of_device_is_status(const struct device_node *device, > return false; > > status = __of_get_property(device, "status", &statlen); > - if (status == NULL) > + if (!status || statlen <= 0) > + return false; > + if (strnlen(status, statlen) >= statlen) > return false; > > - if (statlen > 0) { > - while (*strings) { > - unsigned int len = strlen(*strings); > + while (*strings) { > + unsigned int len = strlen(*strings); > > - if ((*strings)[len - 1] == '-') { > - if (!strncmp(status, *strings, len)) > - return true; > - } else { > - if (!strcmp(status, *strings)) > - return true; > - } > - strings++; > + if ((*strings)[len - 1] == '-') { > + if (!strncmp(status, *strings, len)) > + return true; > + } else { > + if (!strcmp(status, *strings)) > + return true; > } > + strings++; > } > > return false; > @@ -1217,10 +1224,10 @@ EXPORT_SYMBOL(of_find_matching_node_and_match); > int of_alias_from_compatible(const struct device_node *node, char *alias, int len) > { > const char *compatible, *p; > - int cplen; > + int ret; > > - compatible = of_get_property(node, "compatible", &cplen); > - if (!compatible || strlen(compatible) > cplen) > + ret = of_property_read_string_index(node, "compatible", 0, &compatible); > + if (ret) > return -ENODEV; > p = strchr(compatible, ','); > strscpy(alias, p ? p + 1 : compatible, len); > diff --git a/drivers/of/property.c b/drivers/of/property.c > index 50d95d512bf5..edbc7a95aa4c 100644 > --- a/drivers/of/property.c > +++ b/drivers/of/property.c > @@ -648,16 +648,36 @@ EXPORT_SYMBOL_GPL(of_prop_next_u32); > > const char *of_prop_next_string(const struct property *prop, const char *cur) > { > - const void *curv = cur; > + const char *curv = cur; > + const char *end; > + size_t len; > > - if (!prop) > + if (!prop || !prop->value || !prop->length) > return NULL; > > - if (!cur) > + end = prop->value + prop->length; > + > + if (!cur) { > + len = strnlen(prop->value, prop->length); > + if (len >= prop->length) > + return NULL; > + > return prop->value; You return here, but cur is still NULL. So we never advance. We should have a test case in the unit test for this. If not, please add one. > + } > > - curv += strlen(cur) + 1; > - if (curv >= prop->value + prop->length) > + if (cur < (const char *)prop->value || cur >= end) > + return NULL; > + > + len = strnlen(cur, end - cur); > + if (len >= end - cur) > + return NULL; > + > + curv += len + 1; > + if (curv >= end) > + return NULL; > + > + len = strnlen(curv, end - curv); Can we make this so we're not doing strnlen() on each string twice. If return the current string, then 'cur' can point to the next string. > + if (len >= end - curv) > return NULL; > > return curv; > -- > 2.50.1 (Apple Git-155) > > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/2] drivers/of: validate live-tree string properties before string use 2026-04-03 7:32 [PATCH 1/2] drivers/of: validate live-tree string properties before string use Pengpeng Hou 2026-04-03 7:33 ` [PATCH 2/2] drivers/of: validate status properties in reconfig state changes Pengpeng Hou 2026-04-13 17:14 ` [PATCH 1/2] drivers/of: validate live-tree string properties before string use Rob Herring @ 2026-04-17 3:06 ` Pengpeng Hou 2026-04-17 12:36 ` [PATCH v2 " Pengpeng Hou 3 siblings, 0 replies; 11+ messages in thread From: Pengpeng Hou @ 2026-04-17 3:06 UTC (permalink / raw) To: Rob Herring; +Cc: Saravana Kannan, devicetree, linux-kernel, pengpeng Hi Rob, Thanks, I'll respin this. I'll switch the `device_type` match to `of_property_match_string()`, rework `of_prop_next_string()` so the `cur == NULL` case flows through a cleaner iterator path without validating each returned string twice, and add a unittest for that first-iteration case. Thanks, Pengpeng ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 1/2] drivers/of: validate live-tree string properties before string use 2026-04-03 7:32 [PATCH 1/2] drivers/of: validate live-tree string properties before string use Pengpeng Hou ` (2 preceding siblings ...) 2026-04-17 3:06 ` Pengpeng Hou @ 2026-04-17 12:36 ` Pengpeng Hou 2026-04-17 12:40 ` [PATCH v2 2/2] drivers/of: validate status properties in reconfig state changes Pengpeng Hou ` (2 more replies) 3 siblings, 3 replies; 11+ messages in thread From: Pengpeng Hou @ 2026-04-17 12:36 UTC (permalink / raw) To: Rob Herring, Saravana Kannan; +Cc: devicetree, linux-kernel, pengpeng `populate_properties()` stores live-tree property values as raw byte sequences plus a separate `length`. They are not globally guaranteed to be NUL-terminated. `of_prop_next_string()` iterates string-list properties by walking raw bytes, `__of_node_is_type()` checks `device_type`, `__of_device_is_status()` checks `status`, and `of_alias_from_compatible()` reads the first `compatible` entry. These paths must validate that the relevant string fits within the property bounds before they hand it to C string helpers. Validate these live-tree string properties within their declared bounds. In particular, make `of_prop_next_string()` reject malformed entries before returning them, use `of_property_match_string()` for `device_type`, and add unit coverage for malformed first and trailing string-list entries. Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn> --- Changes since v1: - use of_property_match_string() for device_type as suggested by Rob Herring - rework of_prop_next_string() so the first returned string is validated through the same bounded path - add of_unittest_property_string() coverage for malformed first and trailing string-list entries drivers/of/base.c | 36 ++++++++++++---------- drivers/of/property.c | 27 +++++++++++++++++----- drivers/of/unittest.c | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 23 deletions(-) diff --git a/drivers/of/base.c b/drivers/of/base.c index 57420806c1a2..96e4d7a7d5b8 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -82,9 +82,10 @@ EXPORT_SYMBOL(of_node_name_prefix); static bool __of_node_is_type(const struct device_node *np, const char *type) { - const char *match = __of_get_property(np, "device_type", NULL); + if (!np || !type) + return false; - return np && match && type && !strcmp(match, type); + return of_property_match_string(np, "device_type", type) == 0; } #define EXCLUDED_DEFAULT_CELLS_PLATFORMS ( \ @@ -491,22 +492,22 @@ static bool __of_device_is_status(const struct device_node *device, return false; status = __of_get_property(device, "status", &statlen); - if (status == NULL) + if (!status || statlen <= 0) + return false; + if (strnlen(status, statlen) >= statlen) return false; - if (statlen > 0) { - while (*strings) { - unsigned int len = strlen(*strings); + while (*strings) { + unsigned int len = strlen(*strings); - if ((*strings)[len - 1] == '-') { - if (!strncmp(status, *strings, len)) - return true; - } else { - if (!strcmp(status, *strings)) - return true; - } - strings++; + if ((*strings)[len - 1] == '-') { + if (!strncmp(status, *strings, len)) + return true; + } else { + if (!strcmp(status, *strings)) + return true; } + strings++; } return false; @@ -1217,10 +1218,11 @@ EXPORT_SYMBOL(of_find_matching_node_and_match); int of_alias_from_compatible(const struct device_node *node, char *alias, int len) { const char *compatible, *p; - int cplen; + int ret; - compatible = of_get_property(node, "compatible", &cplen); - if (!compatible || strlen(compatible) > cplen) + ret = of_property_read_string_index(node, "compatible", 0, + &compatible); + if (ret) return -ENODEV; p = strchr(compatible, ','); strscpy(alias, p ? p + 1 : compatible, len); diff --git a/drivers/of/property.c b/drivers/of/property.c index 50d95d512bf5..e97bfe357808 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -648,16 +648,31 @@ EXPORT_SYMBOL_GPL(of_prop_next_u32); const char *of_prop_next_string(const struct property *prop, const char *cur) { - const void *curv = cur; + const char *curv; + const char *end; + size_t len; - if (!prop) + if (!prop || !prop->value || !prop->length) return NULL; - if (!cur) - return prop->value; + curv = cur ? cur : prop->value; + end = prop->value + prop->length; - curv += strlen(cur) + 1; - if (curv >= prop->value + prop->length) + if (curv < (const char *)prop->value || curv >= end) + return NULL; + + if (cur) { + len = strnlen(curv, end - curv); + if (len >= end - curv) + return NULL; + + curv += len + 1; + if (curv >= end) + return NULL; + } + + len = strnlen(curv, end - curv); + if (len >= end - curv) return NULL; return curv; diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 29402958f11c..ee53363dfa84 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -713,6 +713,7 @@ static void __init of_unittest_parse_phandle_with_args_map(void) static void __init of_unittest_property_string(void) { const char *strings[4]; + const struct property *prop; struct device_node *np; int rc; @@ -789,6 +790,37 @@ static void __init of_unittest_property_string(void) strings[1] = NULL; rc = of_property_read_string_array(np, "phandle-list-names", strings, 1); unittest(rc == 1 && strings[1] == NULL, "Overwrote end of string array; rc=%i, str='%s'\n", rc, strings[1]); + + /* of_prop_next_string() tests */ + prop = of_find_property(np, "phandle-list-names", NULL); + strings[0] = of_prop_next_string(prop, NULL); + unittest(strings[0] && !strcmp(strings[0], "first"), + "of_prop_next_string() failure; got '%s'\n", strings[0]); + strings[0] = of_prop_next_string(prop, strings[0]); + unittest(strings[0] && !strcmp(strings[0], "second"), + "of_prop_next_string() failure; got '%s'\n", strings[0]); + strings[0] = of_prop_next_string(prop, strings[0]); + unittest(strings[0] && !strcmp(strings[0], "third"), + "of_prop_next_string() failure; got '%s'\n", strings[0]); + strings[0] = of_prop_next_string(prop, strings[0]); + unittest(!strings[0], + "of_prop_next_string() should return NULL at end of list\n"); + + prop = of_find_property(np, "unterminated-string", NULL); + strings[0] = of_prop_next_string(prop, NULL); + unittest(!strings[0], + "of_prop_next_string() should reject unterminated first string\n"); + + prop = of_find_property(np, "unterminated-string-list", NULL); + strings[0] = of_prop_next_string(prop, NULL); + unittest(strings[0] && !strcmp(strings[0], "first"), + "of_prop_next_string() failure; got '%s'\n", strings[0]); + strings[0] = of_prop_next_string(prop, strings[0]); + unittest(strings[0] && !strcmp(strings[0], "second"), + "of_prop_next_string() failure; got '%s'\n", strings[0]); + strings[0] = of_prop_next_string(prop, strings[0]); + unittest(!strings[0], + "of_prop_next_string() should reject unterminated trailing string\n"); } #define propcmp(p1, p2) (((p1)->length == (p2)->length) && \ -- 2.50.1 (Apple Git-155) ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 2/2] drivers/of: validate status properties in reconfig state changes 2026-04-17 12:36 ` [PATCH v2 " Pengpeng Hou @ 2026-04-17 12:40 ` Pengpeng Hou 2026-05-05 18:05 ` [PATCH v2 1/2] drivers/of: validate live-tree string properties before string use Rob Herring 2026-05-07 8:18 ` [PATCH v3 " Pengpeng Hou 2 siblings, 0 replies; 11+ messages in thread From: Pengpeng Hou @ 2026-04-17 12:40 UTC (permalink / raw) To: Rob Herring, Saravana Kannan; +Cc: devicetree, linux-kernel, pengpeng Live-tree reconfiguration properties also carry raw values plus explicit lengths. `of_reconfig_get_state_change()` currently treats `status` property values as NUL-terminated strings and feeds them straight into `strcmp()`. Factor the `"okay"` / `"ok"` check out into a helper that first verifies that the property contains a bounded C string within `prop->length`. Malformed `status` updates should be treated as not enabling the node. Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn> --- Changes since v1: - no change; carried in v2 because patch 1/2 was reworked drivers/of/dynamic.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 1a06175def37..efee59ed371a 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -74,6 +74,20 @@ static const char *action_names[] = { [OF_RECONFIG_UPDATE_PROPERTY] = "UPDATE_PROPERTY", }; +static bool of_property_status_ok(const struct property *prop) +{ + const char *status; + + if (!prop || !prop->value || prop->length <= 0) + return false; + + status = prop->value; + if (strnlen(status, prop->length) >= prop->length) + return false; + + return !strcmp(status, "okay") || !strcmp(status, "ok"); +} + #define _do_print(func, prefix, action, node, prop, ...) ({ \ func("changeset: " prefix "%-15s %pOF%s%s\n", \ ##__VA_ARGS__, action_names[action], node, \ @@ -135,11 +149,9 @@ int of_reconfig_get_state_change(unsigned long action, struct of_reconfig_data * if (prop && !strcmp(prop->name, "status")) { is_status = 1; - status_state = !strcmp(prop->value, "okay") || - !strcmp(prop->value, "ok"); + status_state = of_property_status_ok(prop); if (old_prop) - old_status_state = !strcmp(old_prop->value, "okay") || - !strcmp(old_prop->value, "ok"); + old_status_state = of_property_status_ok(old_prop); } switch (action) { -- 2.50.1 (Apple Git-155) ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v2 1/2] drivers/of: validate live-tree string properties before string use 2026-04-17 12:36 ` [PATCH v2 " Pengpeng Hou 2026-04-17 12:40 ` [PATCH v2 2/2] drivers/of: validate status properties in reconfig state changes Pengpeng Hou @ 2026-05-05 18:05 ` Rob Herring 2026-05-07 8:16 ` Pengpeng Hou 2026-05-07 8:18 ` [PATCH v3 " Pengpeng Hou 2 siblings, 1 reply; 11+ messages in thread From: Rob Herring @ 2026-05-05 18:05 UTC (permalink / raw) To: Pengpeng Hou; +Cc: Saravana Kannan, devicetree, linux-kernel On Fri, Apr 17, 2026 at 08:36:00PM +0800, Pengpeng Hou wrote: > `populate_properties()` stores live-tree property values as raw byte > sequences plus a separate `length`. They are not globally guaranteed to > be NUL-terminated. > > `of_prop_next_string()` iterates string-list properties by walking raw > bytes, `__of_node_is_type()` checks `device_type`, > `__of_device_is_status()` checks `status`, and > `of_alias_from_compatible()` reads the first `compatible` entry. These > paths must validate that the relevant string fits within the property > bounds before they hand it to C string helpers. > > Validate these live-tree string properties within their declared bounds. > In particular, make `of_prop_next_string()` reject malformed entries > before returning them, use `of_property_match_string()` for > `device_type`, and add unit coverage for malformed first and trailing > string-list entries. > > Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn> > --- > Changes since v1: > - use of_property_match_string() for device_type as suggested by > Rob Herring > - rework of_prop_next_string() so the first returned string is validated > through the same bounded path > - add of_unittest_property_string() coverage for malformed first and > trailing string-list entries Did you even test this? The virt machine under QEMU doesn't even boot. [ 0.000000] OF: reserved mem: Reserved memory: No reserved-memory node in the DT It hangs here. [ 0.000000] NUMA: Faking a node at [mem 0x0000000040000000-0x000000007fffffff] ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 1/2] drivers/of: validate live-tree string properties before string use 2026-05-05 18:05 ` [PATCH v2 1/2] drivers/of: validate live-tree string properties before string use Rob Herring @ 2026-05-07 8:16 ` Pengpeng Hou 0 siblings, 0 replies; 11+ messages in thread From: Pengpeng Hou @ 2026-05-07 8:16 UTC (permalink / raw) To: Rob Herring; +Cc: Saravana Kannan, devicetree, linux-kernel, Pengpeng Hou Hi Rob, Thanks for catching this. You are right, I missed a lock recursion in v2: __of_node_is_type() was changed to call of_property_match_string(), but that helper takes devtree_lock. __of_node_is_type() is also used from paths that already hold devtree_lock, including of_find_node_by_type(), of_match_node(), and of_find_matching_node_and_match(), so v2 can deadlock during early boot. I will send a v3 that keeps the device_type string bounded, but uses __of_get_property() directly inside __of_node_is_type() so the helper remains safe under devtree_lock. Sorry about that. Thanks, Pengpeng ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 1/2] drivers/of: validate live-tree string properties before string use 2026-04-17 12:36 ` [PATCH v2 " Pengpeng Hou 2026-04-17 12:40 ` [PATCH v2 2/2] drivers/of: validate status properties in reconfig state changes Pengpeng Hou 2026-05-05 18:05 ` [PATCH v2 1/2] drivers/of: validate live-tree string properties before string use Rob Herring @ 2026-05-07 8:18 ` Pengpeng Hou 2026-05-07 8:18 ` [PATCH v3 2/2] drivers/of: validate status properties in reconfig state changes Pengpeng Hou 2026-05-13 22:18 ` [PATCH v3 1/2] drivers/of: validate live-tree string properties before string use Rob Herring (Arm) 2 siblings, 2 replies; 11+ messages in thread From: Pengpeng Hou @ 2026-05-07 8:18 UTC (permalink / raw) To: Rob Herring, Saravana Kannan; +Cc: devicetree, linux-kernel, Pengpeng Hou `populate_properties()` stores live-tree property values as raw byte sequences plus a separate `length`. They are not globally guaranteed to be NUL-terminated. `of_prop_next_string()` iterates string-list properties by walking raw bytes, `__of_node_is_type()` checks `device_type`, `__of_device_is_status()` checks `status`, and `of_alias_from_compatible()` reads the first `compatible` entry. These paths must validate that the relevant string fits within the property bounds before they hand it to C string helpers. Validate these live-tree string properties within their declared bounds. In particular, make `of_prop_next_string()` reject malformed entries before returning them, keep the `device_type` check inside the existing no-lock helper path, and add unit coverage for malformed first and trailing string-list entries. Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn> --- Changes since v2: - fix the QEMU virt boot hang reported by Rob Herring: v2 made __of_node_is_type() call of_property_match_string(), which takes devtree_lock while __of_node_is_type() is also called from paths that already hold devtree_lock, such as of_find_node_by_type(), of_match_node(), and of_find_matching_node_and_match() - keep the device_type validation bounded, but use __of_get_property() directly so the helper remains safe under devtree_lock Changes since v1: - rework of_prop_next_string() so the first returned string is validated through the same bounded path - add of_unittest_property_string() coverage for malformed first and trailing string-list entries drivers/of/base.c | 43 ++++++++++++++++----------- drivers/of/property.c | 27 +++++++++++++---- drivers/of/unittest.c | 32 ++++++++++++++++++++ 3 files changed, 80 insertions(+), 22 deletions(-) diff --git a/drivers/of/base.c b/drivers/of/base.c index 57420806c1a2..515e364bc4ac 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -82,9 +82,17 @@ EXPORT_SYMBOL(of_node_name_prefix); static bool __of_node_is_type(const struct device_node *np, const char *type) { - const char *match = __of_get_property(np, "device_type", NULL); + const char *match; + int len; - return np && match && type && !strcmp(match, type); + if (!np || !type) + return false; + + match = __of_get_property(np, "device_type", &len); + if (!match || len <= 0 || strnlen(match, len) >= len) + return false; + + return !strcmp(match, type); } #define EXCLUDED_DEFAULT_CELLS_PLATFORMS ( \ @@ -491,22 +499,22 @@ static bool __of_device_is_status(const struct device_node *device, return false; status = __of_get_property(device, "status", &statlen); - if (status == NULL) + if (!status || statlen <= 0) + return false; + if (strnlen(status, statlen) >= statlen) return false; - if (statlen > 0) { - while (*strings) { - unsigned int len = strlen(*strings); + while (*strings) { + unsigned int len = strlen(*strings); - if ((*strings)[len - 1] == '-') { - if (!strncmp(status, *strings, len)) - return true; - } else { - if (!strcmp(status, *strings)) - return true; - } - strings++; + if ((*strings)[len - 1] == '-') { + if (!strncmp(status, *strings, len)) + return true; + } else { + if (!strcmp(status, *strings)) + return true; } + strings++; } return false; @@ -1217,10 +1225,11 @@ EXPORT_SYMBOL(of_find_matching_node_and_match); int of_alias_from_compatible(const struct device_node *node, char *alias, int len) { const char *compatible, *p; - int cplen; + int ret; - compatible = of_get_property(node, "compatible", &cplen); - if (!compatible || strlen(compatible) > cplen) + ret = of_property_read_string_index(node, "compatible", 0, + &compatible); + if (ret) return -ENODEV; p = strchr(compatible, ','); strscpy(alias, p ? p + 1 : compatible, len); diff --git a/drivers/of/property.c b/drivers/of/property.c index 50d95d512bf5..e97bfe357808 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -648,16 +648,31 @@ EXPORT_SYMBOL_GPL(of_prop_next_u32); const char *of_prop_next_string(const struct property *prop, const char *cur) { - const void *curv = cur; + const char *curv; + const char *end; + size_t len; - if (!prop) + if (!prop || !prop->value || !prop->length) return NULL; - if (!cur) - return prop->value; + curv = cur ? cur : prop->value; + end = prop->value + prop->length; - curv += strlen(cur) + 1; - if (curv >= prop->value + prop->length) + if (curv < (const char *)prop->value || curv >= end) + return NULL; + + if (cur) { + len = strnlen(curv, end - curv); + if (len >= end - curv) + return NULL; + + curv += len + 1; + if (curv >= end) + return NULL; + } + + len = strnlen(curv, end - curv); + if (len >= end - curv) return NULL; return curv; diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 29402958f11c..ee53363dfa84 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -713,6 +713,7 @@ static void __init of_unittest_parse_phandle_with_args_map(void) static void __init of_unittest_property_string(void) { const char *strings[4]; + const struct property *prop; struct device_node *np; int rc; @@ -789,6 +790,37 @@ static void __init of_unittest_property_string(void) strings[1] = NULL; rc = of_property_read_string_array(np, "phandle-list-names", strings, 1); unittest(rc == 1 && strings[1] == NULL, "Overwrote end of string array; rc=%i, str='%s'\n", rc, strings[1]); + + /* of_prop_next_string() tests */ + prop = of_find_property(np, "phandle-list-names", NULL); + strings[0] = of_prop_next_string(prop, NULL); + unittest(strings[0] && !strcmp(strings[0], "first"), + "of_prop_next_string() failure; got '%s'\n", strings[0]); + strings[0] = of_prop_next_string(prop, strings[0]); + unittest(strings[0] && !strcmp(strings[0], "second"), + "of_prop_next_string() failure; got '%s'\n", strings[0]); + strings[0] = of_prop_next_string(prop, strings[0]); + unittest(strings[0] && !strcmp(strings[0], "third"), + "of_prop_next_string() failure; got '%s'\n", strings[0]); + strings[0] = of_prop_next_string(prop, strings[0]); + unittest(!strings[0], + "of_prop_next_string() should return NULL at end of list\n"); + + prop = of_find_property(np, "unterminated-string", NULL); + strings[0] = of_prop_next_string(prop, NULL); + unittest(!strings[0], + "of_prop_next_string() should reject unterminated first string\n"); + + prop = of_find_property(np, "unterminated-string-list", NULL); + strings[0] = of_prop_next_string(prop, NULL); + unittest(strings[0] && !strcmp(strings[0], "first"), + "of_prop_next_string() failure; got '%s'\n", strings[0]); + strings[0] = of_prop_next_string(prop, strings[0]); + unittest(strings[0] && !strcmp(strings[0], "second"), + "of_prop_next_string() failure; got '%s'\n", strings[0]); + strings[0] = of_prop_next_string(prop, strings[0]); + unittest(!strings[0], + "of_prop_next_string() should reject unterminated trailing string\n"); } #define propcmp(p1, p2) (((p1)->length == (p2)->length) && \ -- 2.50.1 (Apple Git-155) ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 2/2] drivers/of: validate status properties in reconfig state changes 2026-05-07 8:18 ` [PATCH v3 " Pengpeng Hou @ 2026-05-07 8:18 ` Pengpeng Hou 2026-05-13 22:18 ` [PATCH v3 1/2] drivers/of: validate live-tree string properties before string use Rob Herring (Arm) 1 sibling, 0 replies; 11+ messages in thread From: Pengpeng Hou @ 2026-05-07 8:18 UTC (permalink / raw) To: Rob Herring, Saravana Kannan; +Cc: devicetree, linux-kernel, Pengpeng Hou Live-tree reconfiguration properties also carry raw values plus explicit lengths. `of_reconfig_get_state_change()` currently treats `status` property values as NUL-terminated strings and feeds them straight into `strcmp()`. Factor the `"okay"` / `"ok"` check out into a helper that first verifies that the property contains a bounded C string within `prop->length`. Malformed `status` updates should be treated as not enabling the node. Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn> --- Changes since v2: - no code change; carried with patch 1/2 Changes since v1: - no change; carried in v2 because patch 1/2 was reworked drivers/of/dynamic.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 1a06175def37..efee59ed371a 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -74,6 +74,20 @@ static const char *action_names[] = { [OF_RECONFIG_UPDATE_PROPERTY] = "UPDATE_PROPERTY", }; +static bool of_property_status_ok(const struct property *prop) +{ + const char *status; + + if (!prop || !prop->value || prop->length <= 0) + return false; + + status = prop->value; + if (strnlen(status, prop->length) >= prop->length) + return false; + + return !strcmp(status, "okay") || !strcmp(status, "ok"); +} + #define _do_print(func, prefix, action, node, prop, ...) ({ \ func("changeset: " prefix "%-15s %pOF%s%s\n", \ ##__VA_ARGS__, action_names[action], node, \ @@ -135,11 +149,9 @@ int of_reconfig_get_state_change(unsigned long action, struct of_reconfig_data * if (prop && !strcmp(prop->name, "status")) { is_status = 1; - status_state = !strcmp(prop->value, "okay") || - !strcmp(prop->value, "ok"); + status_state = of_property_status_ok(prop); if (old_prop) - old_status_state = !strcmp(old_prop->value, "okay") || - !strcmp(old_prop->value, "ok"); + old_status_state = of_property_status_ok(old_prop); } switch (action) { -- 2.50.1 (Apple Git-155) ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v3 1/2] drivers/of: validate live-tree string properties before string use 2026-05-07 8:18 ` [PATCH v3 " Pengpeng Hou 2026-05-07 8:18 ` [PATCH v3 2/2] drivers/of: validate status properties in reconfig state changes Pengpeng Hou @ 2026-05-13 22:18 ` Rob Herring (Arm) 1 sibling, 0 replies; 11+ messages in thread From: Rob Herring (Arm) @ 2026-05-13 22:18 UTC (permalink / raw) To: Pengpeng Hou; +Cc: devicetree, Saravana Kannan, linux-kernel On Thu, 07 May 2026 16:18:10 +0800, Pengpeng Hou wrote: > `populate_properties()` stores live-tree property values as raw byte > sequences plus a separate `length`. They are not globally guaranteed to > be NUL-terminated. > > `of_prop_next_string()` iterates string-list properties by walking raw > bytes, `__of_node_is_type()` checks `device_type`, > `__of_device_is_status()` checks `status`, and > `of_alias_from_compatible()` reads the first `compatible` entry. These > paths must validate that the relevant string fits within the property > bounds before they hand it to C string helpers. > > Validate these live-tree string properties within their declared bounds. > In particular, make `of_prop_next_string()` reject malformed entries > before returning them, keep the `device_type` check inside the existing > no-lock helper path, and add unit coverage for malformed first and > trailing string-list entries. > > Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn> > --- > Changes since v2: > - fix the QEMU virt boot hang reported by Rob Herring: v2 made > __of_node_is_type() call of_property_match_string(), which takes > devtree_lock while __of_node_is_type() is also called from paths that > already hold devtree_lock, such as of_find_node_by_type(), > of_match_node(), and of_find_matching_node_and_match() > - keep the device_type validation bounded, but use __of_get_property() > directly so the helper remains safe under devtree_lock > > Changes since v1: > - rework of_prop_next_string() so the first returned string is validated > through the same bounded path > - add of_unittest_property_string() coverage for malformed first and > trailing string-list entries > > drivers/of/base.c | 43 ++++++++++++++++----------- > drivers/of/property.c | 27 +++++++++++++---- > drivers/of/unittest.c | 32 ++++++++++++++++++++ > 3 files changed, 80 insertions(+), 22 deletions(-) > Applied, thanks! ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-05-13 22:18 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-04-03 7:32 [PATCH 1/2] drivers/of: validate live-tree string properties before string use Pengpeng Hou 2026-04-03 7:33 ` [PATCH 2/2] drivers/of: validate status properties in reconfig state changes Pengpeng Hou 2026-04-13 17:14 ` [PATCH 1/2] drivers/of: validate live-tree string properties before string use Rob Herring 2026-04-17 3:06 ` Pengpeng Hou 2026-04-17 12:36 ` [PATCH v2 " Pengpeng Hou 2026-04-17 12:40 ` [PATCH v2 2/2] drivers/of: validate status properties in reconfig state changes Pengpeng Hou 2026-05-05 18:05 ` [PATCH v2 1/2] drivers/of: validate live-tree string properties before string use Rob Herring 2026-05-07 8:16 ` Pengpeng Hou 2026-05-07 8:18 ` [PATCH v3 " Pengpeng Hou 2026-05-07 8:18 ` [PATCH v3 2/2] drivers/of: validate status properties in reconfig state changes Pengpeng Hou 2026-05-13 22:18 ` [PATCH v3 1/2] drivers/of: validate live-tree string properties before string use Rob Herring (Arm)
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.