* [PATCH] libfdt: fdt_get_alias_namelen: Validate aliases
@ 2023-10-07 11:07 Pierre-Clément Tosi
2023-10-08 7:13 ` David Gibson
2023-10-10 14:18 ` Mike McTernan
0 siblings, 2 replies; 6+ messages in thread
From: Pierre-Clément Tosi @ 2023-10-07 11:07 UTC (permalink / raw)
To: David Gibson; +Cc: devicetree-compiler, Mike McTernan, Simon Glass
Ensure that the alias found is valid i.e.
An alias value is a device path and is encoded as a string.
The value represents the full path to a node, ...
This protects against a stack overflow (fdt_path_offset_namelen() calls
fdt_get_alias_namelen() then fdt_path_offset(alias), ...) when /aliases
has an empty property with an empty name.
Co-developed-by: Mike McTernan <mikemcternan@google.com>
Signed-off-by: Pierre-Clément Tosi <ptosi@google.com>
---
libfdt/fdt_ro.c | 11 ++++++++++-
tests/aliases.dts | 3 +++
tests/get_alias.c | 12 +++++++++++-
3 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c
index c4c520c..bda5c0d 100644
--- a/libfdt/fdt_ro.c
+++ b/libfdt/fdt_ro.c
@@ -537,7 +537,16 @@ static const void *fdt_path_getprop_namelen(const void *fdt, const char *path,
const char *fdt_get_alias_namelen(const void *fdt,
const char *name, int namelen)
{
- return fdt_path_getprop_namelen(fdt, "/aliases", name, namelen, NULL);
+ int len;
+ const char *alias;
+
+ alias = fdt_path_getprop_namelen(fdt, "/aliases", name, namelen, &len);
+
+ if (!can_assume(VALID_DTB) &&
+ !(len > 0 && alias && memchr(alias, '\0', len) && *alias == '/'))
+ return NULL;
+
+ return alias;
}
const char *fdt_get_alias(const void *fdt, const char *name)
diff --git a/tests/aliases.dts b/tests/aliases.dts
index 853479a..8820974 100644
--- a/tests/aliases.dts
+++ b/tests/aliases.dts
@@ -8,6 +8,9 @@
s1 = &sub1;
ss1 = &subsub1;
sss1 = &subsubsub1;
+ badpath = "wrong";
+ badpathlong = "wrong/with/parts";
+ empty = "";
};
sub1: subnode@1 {
diff --git a/tests/get_alias.c b/tests/get_alias.c
index fb2c38c..4f3f6fd 100644
--- a/tests/get_alias.c
+++ b/tests/get_alias.c
@@ -21,9 +21,16 @@ static void check_alias(void *fdt, const char *path, const char *alias)
aliaspath = fdt_get_alias(fdt, alias);
- if (path && !aliaspath)
+ if (!path && !aliaspath)
+ return;
+
+ if (!aliaspath)
FAIL("fdt_get_alias(%s) failed\n", alias);
+ if (!path)
+ FAIL("fdt_get_alias(%s) returned %s instead of NULL",
+ alias, aliaspath);
+
if (strcmp(aliaspath, path) != 0)
FAIL("fdt_get_alias(%s) returned %s instead of %s\n",
alias, aliaspath, path);
@@ -36,6 +43,9 @@ int main(int argc, char *argv[])
test_init(argc, argv);
fdt = load_blob_arg(argc, argv);
+ check_alias(fdt, NULL, "badpath");
+ check_alias(fdt, NULL, "badpathlong");
+ check_alias(fdt, NULL, "empty");
check_alias(fdt, "/subnode@1", "s1");
check_alias(fdt, "/subnode@1/subsubnode", "ss1");
check_alias(fdt, "/subnode@1/subsubnode/subsubsubnode", "sss1");
--
2.42.0.609.gbb76f46606-goog
--
Pierre
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH] libfdt: fdt_get_alias_namelen: Validate aliases 2023-10-07 11:07 [PATCH] libfdt: fdt_get_alias_namelen: Validate aliases Pierre-Clément Tosi @ 2023-10-08 7:13 ` David Gibson 2023-10-09 14:09 ` Pierre-Clément Tosi 2023-10-10 14:18 ` Mike McTernan 1 sibling, 1 reply; 6+ messages in thread From: David Gibson @ 2023-10-08 7:13 UTC (permalink / raw) To: Pierre-Clément Tosi; +Cc: devicetree-compiler, Mike McTernan, Simon Glass [-- Attachment #1: Type: text/plain, Size: 4855 bytes --] On Sat, Oct 07, 2023 at 12:07:10PM +0100, Pierre-Clément Tosi wrote: > Ensure that the alias found is valid i.e. > > An alias value is a device path and is encoded as a string. > The value represents the full path to a node, ... Where's that quote from? That is typically the case, but I think that at least back in the old OF days, aliases referring to other aliases was allowed. Even if it's not strictly correct in modern usage, I'd prefer to allow that usage if it's not too difficult to accomplish. > This protects against a stack overflow (fdt_path_offset_namelen() calls > fdt_get_alias_namelen() then fdt_path_offset(alias), ...) when /aliases > has an empty property with an empty name. It would be helpful to spell out that bad situation a bit more precisely. AFAICT the way we discover an alias at the beginning of a path, it would have to have at least one character, which means I'm confused as to how something with an empty name gets in here. > Co-developed-by: Mike McTernan <mikemcternan@google.com> I've never seen a "Co-developed-by" tag before, I'd suggest just putting that in the text. However all developers of the patch should sign off on it, so I'd expect an S-o-b from both people. > Signed-off-by: Pierre-Clément Tosi <ptosi@google.com> > --- > libfdt/fdt_ro.c | 11 ++++++++++- > tests/aliases.dts | 3 +++ > tests/get_alias.c | 12 +++++++++++- > 3 files changed, 24 insertions(+), 2 deletions(-) > > diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c > index c4c520c..bda5c0d 100644 > --- a/libfdt/fdt_ro.c > +++ b/libfdt/fdt_ro.c > @@ -537,7 +537,16 @@ static const void *fdt_path_getprop_namelen(const void *fdt, const char *path, > const char *fdt_get_alias_namelen(const void *fdt, > const char *name, int namelen) > { > - return fdt_path_getprop_namelen(fdt, "/aliases", name, namelen, NULL); > + int len; > + const char *alias; > + > + alias = fdt_path_getprop_namelen(fdt, "/aliases", name, namelen, &len); > + > + if (!can_assume(VALID_DTB) && > + !(len > 0 && alias && memchr(alias, '\0', len) && *alias == '/')) The first three conditions here look good to me. You could even check alias[len - 1] == '\0' to avoid scanning the entire string - an alias should be one single string. I'd prefer not to enforce that the alias in an absolute path (the last check), if we can avoid it, since, as above, I think historically aliases to aliases has been an allowed thing. > + return NULL; Pity we can't return a more specific error here. Oh well. > + > + return alias; > } > > const char *fdt_get_alias(const void *fdt, const char *name) > diff --git a/tests/aliases.dts b/tests/aliases.dts > index 853479a..8820974 100644 > --- a/tests/aliases.dts > +++ b/tests/aliases.dts > @@ -8,6 +8,9 @@ > s1 = &sub1; > ss1 = &subsub1; > sss1 = &subsubsub1; > + badpath = "wrong"; > + badpathlong = "wrong/with/parts"; As well as exercising the absolute path test, which I'd prefer to avoid, there doesn't seem to me that these two cases exercise anything much different from each other. > + empty = ""; This one's a good test. It would be good to test a non-terminated string too (e.g. nonull = [626164]; > }; > > sub1: subnode@1 { > diff --git a/tests/get_alias.c b/tests/get_alias.c > index fb2c38c..4f3f6fd 100644 > --- a/tests/get_alias.c > +++ b/tests/get_alias.c > @@ -21,9 +21,16 @@ static void check_alias(void *fdt, const char *path, const char *alias) > > aliaspath = fdt_get_alias(fdt, alias); > > - if (path && !aliaspath) > + if (!path && !aliaspath) > + return; Not sure what this case is for - we never call this with both values NULL, right? If we did seems like we shouldn't silently ignore it. > + if (!aliaspath) > FAIL("fdt_get_alias(%s) failed\n", alias); Isn't aliaspath == NULL correct for some of these new test cases? You're failing unconditionally here. > + if (!path) > + FAIL("fdt_get_alias(%s) returned %s instead of NULL", > + alias, aliaspath); > + > if (strcmp(aliaspath, path) != 0) > FAIL("fdt_get_alias(%s) returned %s instead of %s\n", > alias, aliaspath, path); > @@ -36,6 +43,9 @@ int main(int argc, char *argv[]) > test_init(argc, argv); > fdt = load_blob_arg(argc, argv); > > + check_alias(fdt, NULL, "badpath"); > + check_alias(fdt, NULL, "badpathlong"); > + check_alias(fdt, NULL, "empty"); > check_alias(fdt, "/subnode@1", "s1"); > check_alias(fdt, "/subnode@1/subsubnode", "ss1"); > check_alias(fdt, "/subnode@1/subsubnode/subsubsubnode", "sss1"); -- David Gibson | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ | _way_ _around_! http://www.ozlabs.org/~dgibson [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] libfdt: fdt_get_alias_namelen: Validate aliases 2023-10-08 7:13 ` David Gibson @ 2023-10-09 14:09 ` Pierre-Clément Tosi 2023-10-10 4:04 ` David Gibson 0 siblings, 1 reply; 6+ messages in thread From: Pierre-Clément Tosi @ 2023-10-09 14:09 UTC (permalink / raw) To: David Gibson; +Cc: devicetree-compiler, Mike McTernan, Simon Glass Hi David, On Sun, Oct 08, 2023 at 06:13:21PM +1100, David Gibson wrote: > On Sat, Oct 07, 2023 at 12:07:10PM +0100, Pierre-Clément Tosi wrote: > > Ensure that the alias found is valid i.e. > > > > An alias value is a device path and is encoded as a string. > > The value represents the full path to a node, ... > > Where's that quote from? That is typically the case, but I think that See the DT specification (v0.4, 3.3 /aliases node): Each property of the /aliases node defines an alias. The property name specifies the alias name. The property value specifies the full path to a node in the devicetree. [...] An alias value is a device path and is encoded as a string. The value represents the full path to a node, [...] > at least back in the old OF days, aliases referring to other aliases > was allowed. Even if it's not strictly correct in modern usage, I'd > prefer to allow that usage if it's not too difficult to accomplish. Compatibility with OF is great but checking that the result is a full path is key to preventing recursion. Otherwise any DT with "circular" aliases can cause the stack overflow I mention below. For example, calling fdt_path_offset(fdt, "loop") on /{ aliases { loop = "loop"; }; }; (I've added this as a test case in v2) > > > This protects against a stack overflow (fdt_path_offset_namelen() calls > > fdt_get_alias_namelen() then fdt_path_offset(alias), ...) when /aliases > > has an empty property with an empty name. > > It would be helpful to spell out that bad situation a bit more > precisely. AFAICT the way we discover an alias at the beginning of a > path, it would have to have at least one character, which means I'm > confused as to how something with an empty name gets in here. I've rephrased the recursion in a more concrete way in v2. AFAICT, nothing currently checks that the "alias has at least one character" and merging [1] will only cover the special "circular" alias of the empty string being aliased to itself. [1]: https://lore.kernel.org/devicetree-compiler/20231006124839.z7auhc3mk37gxios@google.com/ > > > Co-developed-by: Mike McTernan <mikemcternan@google.com> > > I've never seen a "Co-developed-by" tag before, I'd suggest just > putting that in the text. However all developers of the patch should > sign off on it, so I'd expect an S-o-b from both people. > Okay, I now have a link to the original fix in the Android Open Source Project. > > Signed-off-by: Pierre-Clément Tosi <ptosi@google.com> > > --- > > libfdt/fdt_ro.c | 11 ++++++++++- > > tests/aliases.dts | 3 +++ > > tests/get_alias.c | 12 +++++++++++- > > 3 files changed, 24 insertions(+), 2 deletions(-) > > > > diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c > > index c4c520c..bda5c0d 100644 > > --- a/libfdt/fdt_ro.c > > +++ b/libfdt/fdt_ro.c > > @@ -537,7 +537,16 @@ static const void *fdt_path_getprop_namelen(const void *fdt, const char *path, > > const char *fdt_get_alias_namelen(const void *fdt, > > const char *name, int namelen) > > { > > - return fdt_path_getprop_namelen(fdt, "/aliases", name, namelen, NULL); > > + int len; > > + const char *alias; > > + > > + alias = fdt_path_getprop_namelen(fdt, "/aliases", name, namelen, &len); > > + > > + if (!can_assume(VALID_DTB) && > > + !(len > 0 && alias && memchr(alias, '\0', len) && *alias == '/')) > > The first three conditions here look good to me. You could even check > alias[len - 1] == '\0' to avoid scanning the entire string - an alias > should be one single string. > > I'd prefer not to enforce that the alias in an absolute path (the last > check), if we can avoid it, since, as above, I think historically > aliases to aliases has been an allowed thing. I've replaced the memchr() call in v2 but kept the absolute path check for the reason mentioned above. > > > + return NULL; > > Pity we can't return a more specific error here. Oh well. Want me to introduce fdt_find_alias{,_namelen}() wrapped as const char *fdt_get_alias_namelen(...) { const char *alias; int err = fdt_find_alias_namelen(fdt, name, namelen, &alias); return err ? NULL : alias; } ? :) This would allow callers to handle FDT_ERR_NOTFOUND differently from other (probably more serious) errors currently hidden behind a NULL return value and would allow our check to be reported as FDT_ERR_BADVALUE. > > > + > > + return alias; > > } > > > > const char *fdt_get_alias(const void *fdt, const char *name) > > diff --git a/tests/aliases.dts b/tests/aliases.dts > > index 853479a..8820974 100644 > > --- a/tests/aliases.dts > > +++ b/tests/aliases.dts > > @@ -8,6 +8,9 @@ > > s1 = &sub1; > > ss1 = &subsub1; > > sss1 = &subsubsub1; > > + badpath = "wrong"; > > + badpathlong = "wrong/with/parts"; > > As well as exercising the absolute path test, which I'd prefer to > avoid, there doesn't seem to me that these two cases exercise anything > much different from each other. Yes, the idea was that libfdt could get confused by the presence of separators and consider 'badpathlong' valid even if it isn't absolute, hopefully increasing coverage. I have instead merged these cases into a single "relative" test case. > > > + empty = ""; > > This one's a good test. It would be good to test a non-terminated > string too (e.g. nonull = [626164]; Done; do you know of a way to express a property with an empty name in DTS? Or would covering that use-case require a handcrafted DTB? > > > > }; > > > > sub1: subnode@1 { > > diff --git a/tests/get_alias.c b/tests/get_alias.c > > index fb2c38c..4f3f6fd 100644 > > --- a/tests/get_alias.c > > +++ b/tests/get_alias.c > > @@ -21,9 +21,16 @@ static void check_alias(void *fdt, const char *path, const char *alias) > > > > aliaspath = fdt_get_alias(fdt, alias); > > > > - if (path && !aliaspath) > > + if (!path && !aliaspath) > > + return; > > Not sure what this case is for - we never call this with both values > NULL, right? If we did seems like we shouldn't silently ignore it. This returns early if fdt_get_alias() was expected to return NULL and did so. Perhaps, are you confusing 'aliaspath' with 'alias'? > > > + if (!aliaspath) > > FAIL("fdt_get_alias(%s) failed\n", alias); > > Isn't aliaspath == NULL correct for some of these new test cases? Only if 'path' was NULL; see above. > You're failing unconditionally here. > > > + if (!path) > > + FAIL("fdt_get_alias(%s) returned %s instead of NULL", > > + alias, aliaspath); > > + > > if (strcmp(aliaspath, path) != 0) > > FAIL("fdt_get_alias(%s) returned %s instead of %s\n", > > alias, aliaspath, path); > > @@ -36,6 +43,9 @@ int main(int argc, char *argv[]) > > test_init(argc, argv); > > fdt = load_blob_arg(argc, argv); > > > > + check_alias(fdt, NULL, "badpath"); > > + check_alias(fdt, NULL, "badpathlong"); > > + check_alias(fdt, NULL, "empty"); > > check_alias(fdt, "/subnode@1", "s1"); > > check_alias(fdt, "/subnode@1/subsubnode", "ss1"); > > check_alias(fdt, "/subnode@1/subsubnode/subsubsubnode", "sss1"); > > -- > David Gibson | I'll have my music baroque, and my code > david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ > | _way_ _around_! > http://www.ozlabs.org/~dgibson -- Pierre ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] libfdt: fdt_get_alias_namelen: Validate aliases 2023-10-09 14:09 ` Pierre-Clément Tosi @ 2023-10-10 4:04 ` David Gibson 0 siblings, 0 replies; 6+ messages in thread From: David Gibson @ 2023-10-10 4:04 UTC (permalink / raw) To: Pierre-Clément Tosi; +Cc: devicetree-compiler, Mike McTernan, Simon Glass [-- Attachment #1: Type: text/plain, Size: 9458 bytes --] On Mon, Oct 09, 2023 at 03:09:23PM +0100, Pierre-Clément Tosi wrote: > Hi David, > > On Sun, Oct 08, 2023 at 06:13:21PM +1100, David Gibson wrote: > > On Sat, Oct 07, 2023 at 12:07:10PM +0100, Pierre-Clément Tosi wrote: > > > Ensure that the alias found is valid i.e. > > > > > > An alias value is a device path and is encoded as a string. > > > The value represents the full path to a node, ... > > > > Where's that quote from? That is typically the case, but I think that > > See the DT specification (v0.4, 3.3 /aliases node): > > Each property of the /aliases node defines an alias. > The property name specifies the alias name. > The property value specifies the full path to a node in the devicetree. > [...] > An alias value is a device path and is encoded as a string. > The value represents the full path to a node, [...] > > > at least back in the old OF days, aliases referring to other aliases > > was allowed. Even if it's not strictly correct in modern usage, I'd > > prefer to allow that usage if it's not too difficult to accomplish. > > Compatibility with OF is great but checking that the result is a full path is > key to preventing recursion. Ah, that's a fair point. > Otherwise any DT with "circular" aliases can cause > the stack overflow I mention below. For example, calling > fdt_path_offset(fdt, "loop") > > on > > /{ > aliases { > loop = "loop"; > }; > }; Right. I think I got thrown off by the unnecessarily specific description of the loop occurring with an empty alias name, which as you show above doesn't need to be part of the issue. > (I've added this as a test case in v2) > > > > > > This protects against a stack overflow (fdt_path_offset_namelen() calls > > > fdt_get_alias_namelen() then fdt_path_offset(alias), ...) when /aliases > > > has an empty property with an empty name. > > > > It would be helpful to spell out that bad situation a bit more > > precisely. AFAICT the way we discover an alias at the beginning of a > > path, it would have to have at least one character, which means I'm > > confused as to how something with an empty name gets in here. > > I've rephrased the recursion in a more concrete way in v2. > > AFAICT, nothing currently checks that the "alias has at least one character" Well.. not explicitly. And, yes, there are the cases of zero or negative length path addressed by [1]. Apart from those cases, however, we only enter the alias case if path[0] != '/' and the alias name is from the start of the path to the first '/' - therefore the alias must have at least one character. > and merging [1] will only cover the special "circular" alias of the empty string > being aliased to itself. > > [1]: https://lore.kernel.org/devicetree-compiler/20231006124839.z7auhc3mk37gxios@google.com/ > > > > > > Co-developed-by: Mike McTernan <mikemcternan@google.com> > > > > I've never seen a "Co-developed-by" tag before, I'd suggest just > > putting that in the text. However all developers of the patch should > > sign off on it, so I'd expect an S-o-b from both people. > > > > Okay, I now have a link to the original fix in the Android Open Source Project. > > > > Signed-off-by: Pierre-Clément Tosi <ptosi@google.com> > > > --- > > > libfdt/fdt_ro.c | 11 ++++++++++- > > > tests/aliases.dts | 3 +++ > > > tests/get_alias.c | 12 +++++++++++- > > > 3 files changed, 24 insertions(+), 2 deletions(-) > > > > > > diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c > > > index c4c520c..bda5c0d 100644 > > > --- a/libfdt/fdt_ro.c > > > +++ b/libfdt/fdt_ro.c > > > @@ -537,7 +537,16 @@ static const void *fdt_path_getprop_namelen(const void *fdt, const char *path, > > > const char *fdt_get_alias_namelen(const void *fdt, > > > const char *name, int namelen) > > > { > > > - return fdt_path_getprop_namelen(fdt, "/aliases", name, namelen, NULL); > > > + int len; > > > + const char *alias; > > > + > > > + alias = fdt_path_getprop_namelen(fdt, "/aliases", name, namelen, &len); > > > + > > > + if (!can_assume(VALID_DTB) && > > > + !(len > 0 && alias && memchr(alias, '\0', len) && *alias == '/')) > > > > The first three conditions here look good to me. You could even check > > alias[len - 1] == '\0' to avoid scanning the entire string - an alias > > should be one single string. > > > > I'd prefer not to enforce that the alias in an absolute path (the last > > check), if we can avoid it, since, as above, I think historically > > aliases to aliases has been an allowed thing. > > I've replaced the memchr() call in v2 but kept the absolute path check for the > reason mentioned above. > > > > > > + return NULL; > > > > Pity we can't return a more specific error here. Oh well. > > Want me to introduce fdt_find_alias{,_namelen}() wrapped as > > const char *fdt_get_alias_namelen(...) > { > const char *alias; > int err = fdt_find_alias_namelen(fdt, name, namelen, &alias); > return err ? NULL : alias; > } > > ? :) > > This would allow callers to handle FDT_ERR_NOTFOUND differently from other > (probably more serious) errors currently hidden behind a NULL return value and > would allow our check to be reported as FDT_ERR_BADVALUE. No. It's a minor nit that we can't return an error, but I don't think it's worth the conceptual complexity cost of adding an additional entry point to allow for it. > > > > > + > > > + return alias; > > > } > > > > > > const char *fdt_get_alias(const void *fdt, const char *name) > > > diff --git a/tests/aliases.dts b/tests/aliases.dts > > > index 853479a..8820974 100644 > > > --- a/tests/aliases.dts > > > +++ b/tests/aliases.dts > > > @@ -8,6 +8,9 @@ > > > s1 = &sub1; > > > ss1 = &subsub1; > > > sss1 = &subsubsub1; > > > + badpath = "wrong"; > > > + badpathlong = "wrong/with/parts"; > > > > As well as exercising the absolute path test, which I'd prefer to > > avoid, there doesn't seem to me that these two cases exercise anything > > much different from each other. > > Yes, the idea was that libfdt could get confused by the presence of separators > and consider 'badpathlong' valid even if it isn't absolute, hopefully increasing > coverage. I have instead merged these cases into a single "relative" test case. > > > > > > + empty = ""; > > > > This one's a good test. It would be good to test a non-terminated > > string too (e.g. nonull = [626164]; > > Done; do you know of a way to express a property with an empty name in DTS? Or > would covering that use-case require a handcrafted DTB? I don't believe it's possible to express that in dts. At one point I was considering an "escaped" syntax to allow construction of node or property names breaking the usual rules (largely for the purposes of decompiling bad dtbs), but it doesn't look like I ever implemented it. I'd also consider it a bug if dtc emitted a dtb with an empty node or property name (other than the root node, which has an empty name by convention). Although it would be acceptable for that to be a "check" rather than syntactic level check, which means we could still emit such a thing with the -f option, or if the relevant check were explicitly disabled. > > > > > > > }; > > > > > > sub1: subnode@1 { > > > diff --git a/tests/get_alias.c b/tests/get_alias.c > > > index fb2c38c..4f3f6fd 100644 > > > --- a/tests/get_alias.c > > > +++ b/tests/get_alias.c > > > @@ -21,9 +21,16 @@ static void check_alias(void *fdt, const char *path, const char *alias) > > > > > > aliaspath = fdt_get_alias(fdt, alias); > > > > > > - if (path && !aliaspath) > > > + if (!path && !aliaspath) > > > + return; > > > > Not sure what this case is for - we never call this with both values > > NULL, right? If we did seems like we shouldn't silently ignore it. > > This returns early if fdt_get_alias() was expected to return NULL and did so. > Perhaps, are you confusing 'aliaspath' with 'alias'? Oh, yes, sorry. I was misreading it as (!path && !alias). > > > > > + if (!aliaspath) > > > FAIL("fdt_get_alias(%s) failed\n", alias); > > > > Isn't aliaspath == NULL correct for some of these new test cases? > > Only if 'path' was NULL; see above. > > > You're failing unconditionally here. > > > > > + if (!path) > > > + FAIL("fdt_get_alias(%s) returned %s instead of NULL", > > > + alias, aliaspath); > > > + > > > if (strcmp(aliaspath, path) != 0) > > > FAIL("fdt_get_alias(%s) returned %s instead of %s\n", > > > alias, aliaspath, path); > > > @@ -36,6 +43,9 @@ int main(int argc, char *argv[]) > > > test_init(argc, argv); > > > fdt = load_blob_arg(argc, argv); > > > > > > + check_alias(fdt, NULL, "badpath"); > > > + check_alias(fdt, NULL, "badpathlong"); > > > + check_alias(fdt, NULL, "empty"); > > > check_alias(fdt, "/subnode@1", "s1"); > > > check_alias(fdt, "/subnode@1/subsubnode", "ss1"); > > > check_alias(fdt, "/subnode@1/subsubnode/subsubsubnode", "sss1"); > > > > > -- David Gibson | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ | _way_ _around_! http://www.ozlabs.org/~dgibson [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] libfdt: fdt_get_alias_namelen: Validate aliases 2023-10-07 11:07 [PATCH] libfdt: fdt_get_alias_namelen: Validate aliases Pierre-Clément Tosi 2023-10-08 7:13 ` David Gibson @ 2023-10-10 14:18 ` Mike McTernan 2023-10-11 0:36 ` David Gibson 1 sibling, 1 reply; 6+ messages in thread From: Mike McTernan @ 2023-10-10 14:18 UTC (permalink / raw) To: Pierre-Clément Tosi; +Cc: David Gibson, devicetree-compiler, Simon Glass On Sat, 7 Oct 2023 at 12:07, Pierre-Clément Tosi <ptosi@google.com> wrote: > > Ensure that the alias found is valid i.e. > > An alias value is a device path and is encoded as a string. > The value represents the full path to a node, ... > > This protects against a stack overflow (fdt_path_offset_namelen() calls > fdt_get_alias_namelen() then fdt_path_offset(alias), ...) when /aliases > has an empty property with an empty name. > > Co-developed-by: Mike McTernan <mikemcternan@google.com> > Signed-off-by: Pierre-Clément Tosi <ptosi@google.com> Acked-by: Mike McTernan <mikemcternan@google.com> > --- > libfdt/fdt_ro.c | 11 ++++++++++- > tests/aliases.dts | 3 +++ > tests/get_alias.c | 12 +++++++++++- > 3 files changed, 24 insertions(+), 2 deletions(-) > > diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c > index c4c520c..bda5c0d 100644 > --- a/libfdt/fdt_ro.c > +++ b/libfdt/fdt_ro.c > @@ -537,7 +537,16 @@ static const void *fdt_path_getprop_namelen(const void *fdt, const char *path, > const char *fdt_get_alias_namelen(const void *fdt, > const char *name, int namelen) > { > - return fdt_path_getprop_namelen(fdt, "/aliases", name, namelen, NULL); > + int len; > + const char *alias; > + > + alias = fdt_path_getprop_namelen(fdt, "/aliases", name, namelen, &len); > + > + if (!can_assume(VALID_DTB) && > + !(len > 0 && alias && memchr(alias, '\0', len) && *alias == '/')) > + return NULL; > + > + return alias; > } > > const char *fdt_get_alias(const void *fdt, const char *name) > diff --git a/tests/aliases.dts b/tests/aliases.dts > index 853479a..8820974 100644 > --- a/tests/aliases.dts > +++ b/tests/aliases.dts > @@ -8,6 +8,9 @@ > s1 = &sub1; > ss1 = &subsub1; > sss1 = &subsubsub1; > + badpath = "wrong"; > + badpathlong = "wrong/with/parts"; > + empty = ""; > }; > > sub1: subnode@1 { > diff --git a/tests/get_alias.c b/tests/get_alias.c > index fb2c38c..4f3f6fd 100644 > --- a/tests/get_alias.c > +++ b/tests/get_alias.c > @@ -21,9 +21,16 @@ static void check_alias(void *fdt, const char *path, const char *alias) > > aliaspath = fdt_get_alias(fdt, alias); > > - if (path && !aliaspath) > + if (!path && !aliaspath) > + return; > + > + if (!aliaspath) > FAIL("fdt_get_alias(%s) failed\n", alias); > > + if (!path) > + FAIL("fdt_get_alias(%s) returned %s instead of NULL", > + alias, aliaspath); > + > if (strcmp(aliaspath, path) != 0) > FAIL("fdt_get_alias(%s) returned %s instead of %s\n", > alias, aliaspath, path); > @@ -36,6 +43,9 @@ int main(int argc, char *argv[]) > test_init(argc, argv); > fdt = load_blob_arg(argc, argv); > > + check_alias(fdt, NULL, "badpath"); > + check_alias(fdt, NULL, "badpathlong"); > + check_alias(fdt, NULL, "empty"); > check_alias(fdt, "/subnode@1", "s1"); > check_alias(fdt, "/subnode@1/subsubnode", "ss1"); > check_alias(fdt, "/subnode@1/subsubnode/subsubsubnode", "sss1"); > -- > 2.42.0.609.gbb76f46606-goog > > > -- > Pierre ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] libfdt: fdt_get_alias_namelen: Validate aliases 2023-10-10 14:18 ` Mike McTernan @ 2023-10-11 0:36 ` David Gibson 0 siblings, 0 replies; 6+ messages in thread From: David Gibson @ 2023-10-11 0:36 UTC (permalink / raw) To: Mike McTernan; +Cc: Pierre-Clément Tosi, devicetree-compiler, Simon Glass [-- Attachment #1: Type: text/plain, Size: 3842 bytes --] On Tue, Oct 10, 2023 at 03:18:04PM +0100, Mike McTernan wrote: > On Sat, 7 Oct 2023 at 12:07, Pierre-Clément Tosi <ptosi@google.com> wrote: > > > > Ensure that the alias found is valid i.e. > > > > An alias value is a device path and is encoded as a string. > > The value represents the full path to a node, ... > > > > This protects against a stack overflow (fdt_path_offset_namelen() calls > > fdt_get_alias_namelen() then fdt_path_offset(alias), ...) when /aliases > > has an empty property with an empty name. > > > > Co-developed-by: Mike McTernan <mikemcternan@google.com> > > Signed-off-by: Pierre-Clément Tosi <ptosi@google.com> > > Acked-by: Mike McTernan <mikemcternan@google.com> Thanks, I've added this to the patch on merge. > > > --- > > libfdt/fdt_ro.c | 11 ++++++++++- > > tests/aliases.dts | 3 +++ > > tests/get_alias.c | 12 +++++++++++- > > 3 files changed, 24 insertions(+), 2 deletions(-) > > > > diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c > > index c4c520c..bda5c0d 100644 > > --- a/libfdt/fdt_ro.c > > +++ b/libfdt/fdt_ro.c > > @@ -537,7 +537,16 @@ static const void *fdt_path_getprop_namelen(const void *fdt, const char *path, > > const char *fdt_get_alias_namelen(const void *fdt, > > const char *name, int namelen) > > { > > - return fdt_path_getprop_namelen(fdt, "/aliases", name, namelen, NULL); > > + int len; > > + const char *alias; > > + > > + alias = fdt_path_getprop_namelen(fdt, "/aliases", name, namelen, &len); > > + > > + if (!can_assume(VALID_DTB) && > > + !(len > 0 && alias && memchr(alias, '\0', len) && *alias == '/')) > > + return NULL; > > + > > + return alias; > > } > > > > const char *fdt_get_alias(const void *fdt, const char *name) > > diff --git a/tests/aliases.dts b/tests/aliases.dts > > index 853479a..8820974 100644 > > --- a/tests/aliases.dts > > +++ b/tests/aliases.dts > > @@ -8,6 +8,9 @@ > > s1 = &sub1; > > ss1 = &subsub1; > > sss1 = &subsubsub1; > > + badpath = "wrong"; > > + badpathlong = "wrong/with/parts"; > > + empty = ""; > > }; > > > > sub1: subnode@1 { > > diff --git a/tests/get_alias.c b/tests/get_alias.c > > index fb2c38c..4f3f6fd 100644 > > --- a/tests/get_alias.c > > +++ b/tests/get_alias.c > > @@ -21,9 +21,16 @@ static void check_alias(void *fdt, const char *path, const char *alias) > > > > aliaspath = fdt_get_alias(fdt, alias); > > > > - if (path && !aliaspath) > > + if (!path && !aliaspath) > > + return; > > + > > + if (!aliaspath) > > FAIL("fdt_get_alias(%s) failed\n", alias); > > > > + if (!path) > > + FAIL("fdt_get_alias(%s) returned %s instead of NULL", > > + alias, aliaspath); > > + > > if (strcmp(aliaspath, path) != 0) > > FAIL("fdt_get_alias(%s) returned %s instead of %s\n", > > alias, aliaspath, path); > > @@ -36,6 +43,9 @@ int main(int argc, char *argv[]) > > test_init(argc, argv); > > fdt = load_blob_arg(argc, argv); > > > > + check_alias(fdt, NULL, "badpath"); > > + check_alias(fdt, NULL, "badpathlong"); > > + check_alias(fdt, NULL, "empty"); > > check_alias(fdt, "/subnode@1", "s1"); > > check_alias(fdt, "/subnode@1/subsubnode", "ss1"); > > check_alias(fdt, "/subnode@1/subsubnode/subsubsubnode", "sss1"); > > > > > -- David Gibson | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ | _way_ _around_! http://www.ozlabs.org/~dgibson [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2023-10-11 0:38 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2023-10-07 11:07 [PATCH] libfdt: fdt_get_alias_namelen: Validate aliases Pierre-Clément Tosi 2023-10-08 7:13 ` David Gibson 2023-10-09 14:09 ` Pierre-Clément Tosi 2023-10-10 4:04 ` David Gibson 2023-10-10 14:18 ` Mike McTernan 2023-10-11 0:36 ` David Gibson
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).