All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] libfdt: Check that there is only one root node
@ 2021-03-22 23:19 Simon Glass
       [not found] ` <20210322231941.3202986-1-sjg-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
  0 siblings, 1 reply; 4+ messages in thread
From: Simon Glass @ 2021-03-22 23:19 UTC (permalink / raw)
  To: Devicetree Compiler; +Cc: David Gibson, Simon Glass, Arie Haenel, Julien Lenoir

At present it is possible to have two root nodes and even access nodes
in the 'second' root. Such trees should not be considered valid. This
was discovered as part of a security investigation into U-Boot verified
boot.

Add a check for this to fdt_check_full().

Signed-off-by: Simon Glass <sjg-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
Reported-by: Arie Haenel <arie.haenel-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Reported-by: Julien Lenoir <julien.lenoir-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---

 libfdt/fdt_check.c   |  7 +++++++
 tests/Makefile.tests |  4 +++-
 tests/dumptrees.c    |  1 +
 tests/run_tests.sh   |  2 +-
 tests/testdata.h     |  1 +
 tests/trees.S        | 19 +++++++++++++++++++
 6 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/libfdt/fdt_check.c b/libfdt/fdt_check.c
index 9ddfdbf..84870a5 100644
--- a/libfdt/fdt_check.c
+++ b/libfdt/fdt_check.c
@@ -19,6 +19,7 @@ int fdt_check_full(const void *fdt, size_t bufsize)
 	unsigned int depth = 0;
 	const void *prop;
 	const char *propname;
+	bool expect_end = false;
 
 	if (bufsize < FDT_V1_SIZE)
 		return -FDT_ERR_TRUNCATED;
@@ -41,6 +42,10 @@ int fdt_check_full(const void *fdt, size_t bufsize)
 		if (nextoffset < 0)
 			return nextoffset;
 
+		/* If we see two root nodes, something is wrong */
+		if (expect_end && tag != FDT_END)
+			return -FDT_ERR_BADLAYOUT;
+
 		switch (tag) {
 		case FDT_NOP:
 			break;
@@ -60,6 +65,8 @@ int fdt_check_full(const void *fdt, size_t bufsize)
 			if (depth == 0)
 				return -FDT_ERR_BADSTRUCTURE;
 			depth--;
+			if (depth == 0)
+				expect_end = true;
 			break;
 
 		case FDT_PROP:
diff --git a/tests/Makefile.tests b/tests/Makefile.tests
index cb66c9f..fe5cae8 100644
--- a/tests/Makefile.tests
+++ b/tests/Makefile.tests
@@ -32,7 +32,9 @@ LIB_TESTS_L = get_mem_rsv \
 	fs_tree1
 LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%)
 
-LIBTREE_TESTS_L = truncated_property truncated_string truncated_memrsv
+LIBTREE_TESTS_L = truncated_property truncated_string truncated_memrsv \
+	two_roots
+
 LIBTREE_TESTS = $(LIBTREE_TESTS_L:%=$(TESTS_PREFIX)%)
 
 DL_LIB_TESTS_L = asm_tree_dump value-labels
diff --git a/tests/dumptrees.c b/tests/dumptrees.c
index aecb326..02ca092 100644
--- a/tests/dumptrees.c
+++ b/tests/dumptrees.c
@@ -24,6 +24,7 @@ static struct {
 	TREE(ovf_size_strings),
 	TREE(truncated_property), TREE(truncated_string),
 	TREE(truncated_memrsv),
+	TREE(two_roots)
 };
 
 #define NUM_TREES	(sizeof(trees) / sizeof(trees[0]))
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 4b8dada..82543fc 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -518,7 +518,7 @@ libfdt_tests () {
 	run_test check_full $good
     done
     for bad in truncated_property.dtb truncated_string.dtb \
-				      truncated_memrsv.dtb; do
+		truncated_memrsv.dtb two_roots.dtb; do
 	run_test check_full -n $bad
     done
 }
diff --git a/tests/testdata.h b/tests/testdata.h
index 0d08efb..d03f352 100644
--- a/tests/testdata.h
+++ b/tests/testdata.h
@@ -55,4 +55,5 @@ extern struct fdt_header bad_prop_char;
 extern struct fdt_header ovf_size_strings;
 extern struct fdt_header truncated_string;
 extern struct fdt_header truncated_memrsv;
+extern struct fdt_header two_roots;
 #endif /* ! __ASSEMBLY */
diff --git a/tests/trees.S b/tests/trees.S
index efab287..e2380b7 100644
--- a/tests/trees.S
+++ b/tests/trees.S
@@ -279,3 +279,22 @@ truncated_memrsv_rsvmap:
 truncated_memrsv_rsvmap_end:
 
 truncated_memrsv_end:
+
+
+        /* two root nodes */
+        TREE_HDR(two_roots)
+	EMPTY_RSVMAP(two_roots)
+
+two_roots_struct:
+	BEGIN_NODE("")
+	END_NODE
+	BEGIN_NODE("")
+	END_NODE
+	FDTLONG(FDT_END)
+two_roots_struct_end:
+
+two_roots_strings:
+two_roots_strings_end:
+
+two_roots_end:
+
-- 
2.31.0.rc2.261.g7f71774620-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/2] libfdt: Check that the root-node name is empty
       [not found] ` <20210322231941.3202986-1-sjg-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
@ 2021-03-22 23:19   ` Simon Glass
       [not found]     ` <20210322231941.3202986-2-sjg-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
  2021-03-23  0:03   ` [PATCH 1/2] libfdt: Check that there is only one root node David Gibson
  1 sibling, 1 reply; 4+ messages in thread
From: Simon Glass @ 2021-03-22 23:19 UTC (permalink / raw)
  To: Devicetree Compiler; +Cc: David Gibson, Simon Glass, Arie Haenel, Julien Lenoir

The root node is supposed to have an empty name, but at present this is
not checked. The behaviour of such a tree is not well defined. Most
software rightly assumes that the root node is at offset 0 and does not
check the name. This oddity was discovered as part of a security
investigation into U-Boot verified boot.

Add a check for this to fdt_check_full().

Signed-off-by: Simon Glass <sjg-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
Reported-by: Arie Haenel <arie.haenel-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Reported-by: Julien Lenoir <julien.lenoir-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---

 libfdt/fdt_check.c   | 10 ++++++++++
 tests/Makefile.tests |  2 +-
 tests/dumptrees.c    |  3 ++-
 tests/testdata.h     |  1 +
 tests/trees.S        | 15 +++++++++++++++
 5 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/libfdt/fdt_check.c b/libfdt/fdt_check.c
index 84870a5..d3a27c5 100644
--- a/libfdt/fdt_check.c
+++ b/libfdt/fdt_check.c
@@ -59,6 +59,16 @@ int fdt_check_full(const void *fdt, size_t bufsize)
 			depth++;
 			if (depth > INT_MAX)
 				return -FDT_ERR_BADSTRUCTURE;
+
+			/* The root node must have an empty name */
+			if (depth == 1) {
+				const char *name;
+				int len;
+
+				name = fdt_get_name(fdt, offset, &len);
+				if (*name || len)
+					return -FDT_ERR_BADLAYOUT;
+			}
 			break;
 
 		case FDT_END_NODE:
diff --git a/tests/Makefile.tests b/tests/Makefile.tests
index fe5cae8..2b47627 100644
--- a/tests/Makefile.tests
+++ b/tests/Makefile.tests
@@ -33,7 +33,7 @@ LIB_TESTS_L = get_mem_rsv \
 LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%)
 
 LIBTREE_TESTS_L = truncated_property truncated_string truncated_memrsv \
-	two_roots
+	two_roots named_root
 
 LIBTREE_TESTS = $(LIBTREE_TESTS_L:%=$(TESTS_PREFIX)%)
 
diff --git a/tests/dumptrees.c b/tests/dumptrees.c
index 02ca092..f1e0ea9 100644
--- a/tests/dumptrees.c
+++ b/tests/dumptrees.c
@@ -24,7 +24,8 @@ static struct {
 	TREE(ovf_size_strings),
 	TREE(truncated_property), TREE(truncated_string),
 	TREE(truncated_memrsv),
-	TREE(two_roots)
+	TREE(two_roots),
+	TREE(named_root)
 };
 
 #define NUM_TREES	(sizeof(trees) / sizeof(trees[0]))
diff --git a/tests/testdata.h b/tests/testdata.h
index d03f352..4f9e3ba 100644
--- a/tests/testdata.h
+++ b/tests/testdata.h
@@ -56,4 +56,5 @@ extern struct fdt_header ovf_size_strings;
 extern struct fdt_header truncated_string;
 extern struct fdt_header truncated_memrsv;
 extern struct fdt_header two_roots;
+extern struct fdt_header named_root;
 #endif /* ! __ASSEMBLY */
diff --git a/tests/trees.S b/tests/trees.S
index e2380b7..95d599d 100644
--- a/tests/trees.S
+++ b/tests/trees.S
@@ -298,3 +298,18 @@ two_roots_strings_end:
 
 two_roots_end:
 
+
+        /* root node with a non-empty name */
+        TREE_HDR(named_root)
+	EMPTY_RSVMAP(named_root)
+
+named_root_struct:
+	BEGIN_NODE("fake")
+	END_NODE
+	FDTLONG(FDT_END)
+named_root_struct_end:
+
+named_root_strings:
+named_root_strings_end:
+
+named_root_end:
-- 
2.31.0.rc2.261.g7f71774620-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH 1/2] libfdt: Check that there is only one root node
       [not found] ` <20210322231941.3202986-1-sjg-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
  2021-03-22 23:19   ` [PATCH 2/2] libfdt: Check that the root-node name is empty Simon Glass
@ 2021-03-23  0:03   ` David Gibson
  1 sibling, 0 replies; 4+ messages in thread
From: David Gibson @ 2021-03-23  0:03 UTC (permalink / raw)
  To: Simon Glass; +Cc: Devicetree Compiler, Arie Haenel, Julien Lenoir

[-- Attachment #1: Type: text/plain, Size: 4512 bytes --]

On Tue, Mar 23, 2021 at 12:19:40PM +1300, Simon Glass wrote:
> At present it is possible to have two root nodes and even access nodes
> in the 'second' root. Such trees should not be considered valid. This
> was discovered as part of a security investigation into U-Boot verified
> boot.
> 
> Add a check for this to fdt_check_full().
> 
> Signed-off-by: Simon Glass <sjg-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
> Reported-by: Arie Haenel <arie.haenel-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> Reported-by: Julien Lenoir <julien.lenoir-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> ---
> 
>  libfdt/fdt_check.c   |  7 +++++++
>  tests/Makefile.tests |  4 +++-
>  tests/dumptrees.c    |  1 +
>  tests/run_tests.sh   |  2 +-
>  tests/testdata.h     |  1 +
>  tests/trees.S        | 19 +++++++++++++++++++
>  6 files changed, 32 insertions(+), 2 deletions(-)
> 
> diff --git a/libfdt/fdt_check.c b/libfdt/fdt_check.c
> index 9ddfdbf..84870a5 100644
> --- a/libfdt/fdt_check.c
> +++ b/libfdt/fdt_check.c
> @@ -19,6 +19,7 @@ int fdt_check_full(const void *fdt, size_t bufsize)
>  	unsigned int depth = 0;
>  	const void *prop;
>  	const char *propname;
> +	bool expect_end = false;
>  
>  	if (bufsize < FDT_V1_SIZE)
>  		return -FDT_ERR_TRUNCATED;
> @@ -41,6 +42,10 @@ int fdt_check_full(const void *fdt, size_t bufsize)
>  		if (nextoffset < 0)
>  			return nextoffset;
>  
> +		/* If we see two root nodes, something is wrong */
> +		if (expect_end && tag != FDT_END)
> +			return -FDT_ERR_BADLAYOUT;

This should be -FDT_ERR_BADSTRUCTURE (since it's an error in the
format of the structure block), but otherwise this patch LGTM.

> +
>  		switch (tag) {
>  		case FDT_NOP:
>  			break;
> @@ -60,6 +65,8 @@ int fdt_check_full(const void *fdt, size_t bufsize)
>  			if (depth == 0)
>  				return -FDT_ERR_BADSTRUCTURE;
>  			depth--;
> +			if (depth == 0)
> +				expect_end = true;
>  			break;
>  
>  		case FDT_PROP:
> diff --git a/tests/Makefile.tests b/tests/Makefile.tests
> index cb66c9f..fe5cae8 100644
> --- a/tests/Makefile.tests
> +++ b/tests/Makefile.tests
> @@ -32,7 +32,9 @@ LIB_TESTS_L = get_mem_rsv \
>  	fs_tree1
>  LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%)
>  
> -LIBTREE_TESTS_L = truncated_property truncated_string truncated_memrsv
> +LIBTREE_TESTS_L = truncated_property truncated_string truncated_memrsv \
> +	two_roots
> +
>  LIBTREE_TESTS = $(LIBTREE_TESTS_L:%=$(TESTS_PREFIX)%)
>  
>  DL_LIB_TESTS_L = asm_tree_dump value-labels
> diff --git a/tests/dumptrees.c b/tests/dumptrees.c
> index aecb326..02ca092 100644
> --- a/tests/dumptrees.c
> +++ b/tests/dumptrees.c
> @@ -24,6 +24,7 @@ static struct {
>  	TREE(ovf_size_strings),
>  	TREE(truncated_property), TREE(truncated_string),
>  	TREE(truncated_memrsv),
> +	TREE(two_roots)
>  };
>  
>  #define NUM_TREES	(sizeof(trees) / sizeof(trees[0]))
> diff --git a/tests/run_tests.sh b/tests/run_tests.sh
> index 4b8dada..82543fc 100755
> --- a/tests/run_tests.sh
> +++ b/tests/run_tests.sh
> @@ -518,7 +518,7 @@ libfdt_tests () {
>  	run_test check_full $good
>      done
>      for bad in truncated_property.dtb truncated_string.dtb \
> -				      truncated_memrsv.dtb; do
> +		truncated_memrsv.dtb two_roots.dtb; do
>  	run_test check_full -n $bad
>      done
>  }
> diff --git a/tests/testdata.h b/tests/testdata.h
> index 0d08efb..d03f352 100644
> --- a/tests/testdata.h
> +++ b/tests/testdata.h
> @@ -55,4 +55,5 @@ extern struct fdt_header bad_prop_char;
>  extern struct fdt_header ovf_size_strings;
>  extern struct fdt_header truncated_string;
>  extern struct fdt_header truncated_memrsv;
> +extern struct fdt_header two_roots;
>  #endif /* ! __ASSEMBLY */
> diff --git a/tests/trees.S b/tests/trees.S
> index efab287..e2380b7 100644
> --- a/tests/trees.S
> +++ b/tests/trees.S
> @@ -279,3 +279,22 @@ truncated_memrsv_rsvmap:
>  truncated_memrsv_rsvmap_end:
>  
>  truncated_memrsv_end:
> +
> +
> +        /* two root nodes */
> +        TREE_HDR(two_roots)
> +	EMPTY_RSVMAP(two_roots)
> +
> +two_roots_struct:
> +	BEGIN_NODE("")
> +	END_NODE
> +	BEGIN_NODE("")
> +	END_NODE
> +	FDTLONG(FDT_END)
> +two_roots_struct_end:
> +
> +two_roots_strings:
> +two_roots_strings_end:
> +
> +two_roots_end:
> +

-- 
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] 4+ messages in thread

* Re: [PATCH 2/2] libfdt: Check that the root-node name is empty
       [not found]     ` <20210322231941.3202986-2-sjg-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
@ 2021-03-23  0:03       ` David Gibson
  0 siblings, 0 replies; 4+ messages in thread
From: David Gibson @ 2021-03-23  0:03 UTC (permalink / raw)
  To: Simon Glass; +Cc: Devicetree Compiler, Arie Haenel, Julien Lenoir

[-- Attachment #1: Type: text/plain, Size: 3599 bytes --]

On Tue, Mar 23, 2021 at 12:19:41PM +1300, Simon Glass wrote:
> The root node is supposed to have an empty name, but at present this is
> not checked. The behaviour of such a tree is not well defined. Most
> software rightly assumes that the root node is at offset 0 and does not
> check the name. This oddity was discovered as part of a security
> investigation into U-Boot verified boot.
> 
> Add a check for this to fdt_check_full().
> 
> Signed-off-by: Simon Glass <sjg-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
> Reported-by: Arie Haenel <arie.haenel-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> Reported-by: Julien Lenoir <julien.lenoir-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> ---
> 
>  libfdt/fdt_check.c   | 10 ++++++++++
>  tests/Makefile.tests |  2 +-
>  tests/dumptrees.c    |  3 ++-
>  tests/testdata.h     |  1 +
>  tests/trees.S        | 15 +++++++++++++++
>  5 files changed, 29 insertions(+), 2 deletions(-)
> 
> diff --git a/libfdt/fdt_check.c b/libfdt/fdt_check.c
> index 84870a5..d3a27c5 100644
> --- a/libfdt/fdt_check.c
> +++ b/libfdt/fdt_check.c
> @@ -59,6 +59,16 @@ int fdt_check_full(const void *fdt, size_t bufsize)
>  			depth++;
>  			if (depth > INT_MAX)
>  				return -FDT_ERR_BADSTRUCTURE;
> +
> +			/* The root node must have an empty name */
> +			if (depth == 1) {
> +				const char *name;
> +				int len;
> +
> +				name = fdt_get_name(fdt, offset, &len);
> +				if (*name || len)
> +					return -FDT_ERR_BADLAYOUT;

Again, this should be BADSTRUCTURE, not BADLAYOUT.  Otherwise LGTM.

> +			}
>  			break;
>  
>  		case FDT_END_NODE:
> diff --git a/tests/Makefile.tests b/tests/Makefile.tests
> index fe5cae8..2b47627 100644
> --- a/tests/Makefile.tests
> +++ b/tests/Makefile.tests
> @@ -33,7 +33,7 @@ LIB_TESTS_L = get_mem_rsv \
>  LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%)
>  
>  LIBTREE_TESTS_L = truncated_property truncated_string truncated_memrsv \
> -	two_roots
> +	two_roots named_root
>  
>  LIBTREE_TESTS = $(LIBTREE_TESTS_L:%=$(TESTS_PREFIX)%)
>  
> diff --git a/tests/dumptrees.c b/tests/dumptrees.c
> index 02ca092..f1e0ea9 100644
> --- a/tests/dumptrees.c
> +++ b/tests/dumptrees.c
> @@ -24,7 +24,8 @@ static struct {
>  	TREE(ovf_size_strings),
>  	TREE(truncated_property), TREE(truncated_string),
>  	TREE(truncated_memrsv),
> -	TREE(two_roots)
> +	TREE(two_roots),
> +	TREE(named_root)
>  };
>  
>  #define NUM_TREES	(sizeof(trees) / sizeof(trees[0]))
> diff --git a/tests/testdata.h b/tests/testdata.h
> index d03f352..4f9e3ba 100644
> --- a/tests/testdata.h
> +++ b/tests/testdata.h
> @@ -56,4 +56,5 @@ extern struct fdt_header ovf_size_strings;
>  extern struct fdt_header truncated_string;
>  extern struct fdt_header truncated_memrsv;
>  extern struct fdt_header two_roots;
> +extern struct fdt_header named_root;
>  #endif /* ! __ASSEMBLY */
> diff --git a/tests/trees.S b/tests/trees.S
> index e2380b7..95d599d 100644
> --- a/tests/trees.S
> +++ b/tests/trees.S
> @@ -298,3 +298,18 @@ two_roots_strings_end:
>  
>  two_roots_end:
>  
> +
> +        /* root node with a non-empty name */
> +        TREE_HDR(named_root)
> +	EMPTY_RSVMAP(named_root)
> +
> +named_root_struct:
> +	BEGIN_NODE("fake")
> +	END_NODE
> +	FDTLONG(FDT_END)
> +named_root_struct_end:
> +
> +named_root_strings:
> +named_root_strings_end:
> +
> +named_root_end:

-- 
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] 4+ messages in thread

end of thread, other threads:[~2021-03-23  0:03 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-22 23:19 [PATCH 1/2] libfdt: Check that there is only one root node Simon Glass
     [not found] ` <20210322231941.3202986-1-sjg-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
2021-03-22 23:19   ` [PATCH 2/2] libfdt: Check that the root-node name is empty Simon Glass
     [not found]     ` <20210322231941.3202986-2-sjg-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
2021-03-23  0:03       ` David Gibson
2021-03-23  0:03   ` [PATCH 1/2] libfdt: Check that there is only one root node David Gibson

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.