Re: [PATCH] libfdt: fdt_get_alias_namelen: Validate aliases

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]



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@xxxxxxxxxx>

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@xxxxxxxxxx>
> ---
>  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: signature.asc
Description: PGP signature


[Index of Archives]     [Device Tree]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux