All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] libfdt: Check for integer overflows
@ 2021-06-11 11:58 Andre Przywara
       [not found] ` <20210611115823.31583-1-andre.przywara-5wv7dgnIgG8@public.gmane.org>
  0 siblings, 1 reply; 9+ messages in thread
From: Andre Przywara @ 2021-06-11 11:58 UTC (permalink / raw)
  To: David Gibson, devicetree-compiler-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Simon Glass

Apparently some time ago some kind of audit or review of the Linux kernel
triggered some complaints about potential integer overflows in
libfdt[1][2]. The proposed patches there were not of good quality, and
never found their way into any upstream project anyway. This seemed to
assume fabricated DT blobs to upset libfdt, not sure how feasible this
scenario is in general.

This series tries to address the same issues, but hopefully in a better
way. The focus is on fdt_open_into(), since this operates on user
provided (writable) buffers, and a wrong offset could do some harm here.

I am not sure these patches here are the right solution, or whether we
should pursue a more general approach: Many problems stem from the
redundancy and so potential inconsistency of DTB header fields, namely
the total size, the various offsets and the various size fields.
fdt_check_header() seems to detect some of these issues, but it is not
used at the moment in fdt_open_into(). So we could add a call in
fdt_open_into(), or extend FDT_RO_PROBE() to also check for those issues.
Let me know what you think.

Because it was easy to do, patch 2/4 adds a test for one issue I could
reproduce (fixed by patch 1/4). I have some custom DTBs and tests to
catch the problem that patch 4/4 fixes, but need more time to write a
proper test case for this.

Cheers,
Andre

[1] https://nvd.nist.gov/vuln/detail/CVE-2014-9801
[2] https://nvd.nist.gov/vuln/detail/CVE-2014-9802

Andre Przywara (4):
  libfdt: Check for invalid memreserve block
  tests: Enhance truncated_memrsv to test fdt_open_into()
  libfdt: Check DT struct size for integer overflow
  libfdt: Protect buffer size checks against integer overflow

 libfdt/fdt_rw.c          | 63 +++++++++++++++++++++++++++++-----------
 tests/truncated_memrsv.c | 10 ++++++-
 2 files changed, 55 insertions(+), 18 deletions(-)

-- 
2.17.5


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

* [PATCH 1/4] libfdt: Check for invalid memreserve block
       [not found] ` <20210611115823.31583-1-andre.przywara-5wv7dgnIgG8@public.gmane.org>
@ 2021-06-11 11:58   ` Andre Przywara
       [not found]     ` <20210611115823.31583-2-andre.przywara-5wv7dgnIgG8@public.gmane.org>
  2021-06-11 11:58   ` [PATCH 2/4] tests: Enhance truncated_memrsv to test fdt_open_into() Andre Przywara
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 9+ messages in thread
From: Andre Przywara @ 2021-06-11 11:58 UTC (permalink / raw)
  To: David Gibson, devicetree-compiler-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Simon Glass

fdt_num_mem_rsv() can return a negative error value, when the memreserve
block is not properly terminated or is exceeding its advertised size,
This can happen on malformed DTBs, and FDT_RO_PROBE() does not cover this.

Check the return value of fdt_num_mem_rsv() first for an error
condition, and also check if the total size of the memreserve block
would provoke a signed integer overflow.

This fixes issues with malformed DT blobs, where we would end up with
negative offsets into buffers.

Signed-off-by: Andre Przywara <andre.przywara-5wv7dgnIgG8@public.gmane.org>
---
 libfdt/fdt_rw.c | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c
index 3621d36..a0f03d0 100644
--- a/libfdt/fdt_rw.c
+++ b/libfdt/fdt_rw.c
@@ -427,8 +427,13 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize)
 
 	FDT_RO_PROBE(fdt);
 
-	mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
-		* sizeof(struct fdt_reserve_entry);
+	mem_rsv_size = fdt_num_mem_rsv(fdt);
+	if (mem_rsv_size < 0)
+		return mem_rsv_size;
+
+	mem_rsv_size = (mem_rsv_size + 1) * sizeof(struct fdt_reserve_entry);
+	if (!can_assume(VALID_DTB) && mem_rsv_size < 0)
+		return -FDT_ERR_NOSPACE;
 
 	if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
 		struct_size = fdt_size_dt_struct(fdt);
@@ -490,8 +495,14 @@ int fdt_pack(void *fdt)
 
 	FDT_RW_PROBE(fdt);
 
-	mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
-		* sizeof(struct fdt_reserve_entry);
+	mem_rsv_size = fdt_num_mem_rsv(fdt);
+	if (mem_rsv_size < 0)
+		return mem_rsv_size;
+
+	mem_rsv_size = (mem_rsv_size + 1) * sizeof(struct fdt_reserve_entry);
+	if (!can_assume(VALID_DTB) && mem_rsv_size < 0)
+		return -FDT_ERR_NOSPACE;
+
 	fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt),
 			fdt_size_dt_strings(fdt));
 	fdt_set_totalsize(fdt, fdt_data_size_(fdt));
-- 
2.17.5


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

* [PATCH 2/4] tests: Enhance truncated_memrsv to test fdt_open_into()
       [not found] ` <20210611115823.31583-1-andre.przywara-5wv7dgnIgG8@public.gmane.org>
  2021-06-11 11:58   ` [PATCH 1/4] libfdt: Check for invalid memreserve block Andre Przywara
@ 2021-06-11 11:58   ` Andre Przywara
       [not found]     ` <20210611115823.31583-3-andre.przywara-5wv7dgnIgG8@public.gmane.org>
  2021-06-11 11:58   ` [PATCH 3/4] libfdt: Check DT struct size for integer overflow Andre Przywara
  2021-06-11 11:58   ` [PATCH 4/4] libfdt: Protect buffer size checks against " Andre Przywara
  3 siblings, 1 reply; 9+ messages in thread
From: Andre Przywara @ 2021-06-11 11:58 UTC (permalink / raw)
  To: David Gibson, devicetree-compiler-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Simon Glass

We already check that libfdt's r/o functions can deal properly with
malformed blobs, where the memreserve block is not correct.

Add a test for fdt_open_into(), which also queries the memreserve block,
and uses the return value as an offset into a buffer.

Signed-off-by: Andre Przywara <andre.przywara-5wv7dgnIgG8@public.gmane.org>
---
 tests/truncated_memrsv.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/tests/truncated_memrsv.c b/tests/truncated_memrsv.c
index d78036c..9defef3 100644
--- a/tests/truncated_memrsv.c
+++ b/tests/truncated_memrsv.c
@@ -18,7 +18,8 @@
 int main(int argc, char *argv[])
 {
 	void *fdt = &truncated_memrsv;
-	int err;
+	char *buf;
+	int err, bufsize;
 	uint64_t addr, size;
 
 	test_init(argc, argv);
@@ -46,5 +47,12 @@ int main(int argc, char *argv[])
 		FAIL("fdt_get_mem_rsv(1) returned %d instead of -FDT_ERR_BADOFFSET",
 		     err);
 
+	bufsize = fdt_totalsize(fdt);
+	buf = xmalloc(bufsize);
+	err = fdt_open_into(fdt, buf, bufsize);
+	if (err != -FDT_ERR_TRUNCATED)
+		FAIL("fdt_open_into() returned %d instead of -FDT_ERR_TRUNCATED",
+		     err);
+
 	PASS();
 }
-- 
2.17.5


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

* [PATCH 3/4] libfdt: Check DT struct size for integer overflow
       [not found] ` <20210611115823.31583-1-andre.przywara-5wv7dgnIgG8@public.gmane.org>
  2021-06-11 11:58   ` [PATCH 1/4] libfdt: Check for invalid memreserve block Andre Przywara
  2021-06-11 11:58   ` [PATCH 2/4] tests: Enhance truncated_memrsv to test fdt_open_into() Andre Przywara
@ 2021-06-11 11:58   ` Andre Przywara
       [not found]     ` <20210611115823.31583-4-andre.przywara-5wv7dgnIgG8@public.gmane.org>
  2021-06-11 11:58   ` [PATCH 4/4] libfdt: Protect buffer size checks against " Andre Przywara
  3 siblings, 1 reply; 9+ messages in thread
From: Andre Przywara @ 2021-06-11 11:58 UTC (permalink / raw)
  To: David Gibson, devicetree-compiler-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Simon Glass

The DTB header fields store unsigned values for size and offset, however
we have a 2 GB limit on the overall size, since we use signed "node"
offsets everywhere.

As fdt_open_into() is no exception here, check that the advertised DT
structure size fits in an int, before using that value as an offset into
a buffer.

Signed-off-by: Andre Przywara <andre.przywara-5wv7dgnIgG8@public.gmane.org>
---
 libfdt/fdt_rw.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c
index a0f03d0..062edcc 100644
--- a/libfdt/fdt_rw.c
+++ b/libfdt/fdt_rw.c
@@ -437,6 +437,8 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize)
 
 	if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
 		struct_size = fdt_size_dt_struct(fdt);
+		if (!can_assume(VALID_DTB) && struct_size < 0)
+			return -FDT_ERR_NOSPACE;
 	} else if (fdt_version(fdt) == 16) {
 		struct_size = 0;
 		while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
-- 
2.17.5


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

* [PATCH 4/4] libfdt: Protect buffer size checks against integer overflow
       [not found] ` <20210611115823.31583-1-andre.przywara-5wv7dgnIgG8@public.gmane.org>
                     ` (2 preceding siblings ...)
  2021-06-11 11:58   ` [PATCH 3/4] libfdt: Check DT struct size for integer overflow Andre Przywara
@ 2021-06-11 11:58   ` Andre Przywara
       [not found]     ` <20210611115823.31583-5-andre.przywara-5wv7dgnIgG8@public.gmane.org>
  3 siblings, 1 reply; 9+ messages in thread
From: Andre Przywara @ 2021-06-11 11:58 UTC (permalink / raw)
  To: David Gibson, devicetree-compiler-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Simon Glass

In fdt_open_into() and fdt_packblocks_(), and in most other libfdt
functions for that matter, we use signed values for blob offsets and
sizes. When adding several signed values together, we should check for
integer overflows, as several valid sizes in inself can easily exceed
INT_MAX when summed up.

This is particularly important here, as we use the sum to check against
the maximum buffer size, or use them directly as buffer offsets.
Any negative values would not end up well.

Check for negative values after adding each size value separately. When
VALID_DTB is defined, the compiler should optimise this to the former
joint addition, so it should not increase the code size in this case.

Signed-off-by: Andre Przywara <andre.przywara-5wv7dgnIgG8@public.gmane.org>
---
 libfdt/fdt_rw.c | 42 +++++++++++++++++++++++++++++-------------
 1 file changed, 29 insertions(+), 13 deletions(-)

diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c
index 062edcc..24aff27 100644
--- a/libfdt/fdt_rw.c
+++ b/libfdt/fdt_rw.c
@@ -393,16 +393,20 @@ int fdt_del_node(void *fdt, int nodeoffset)
 				  endoffset - nodeoffset, 0);
 }
 
-static void fdt_packblocks_(const char *old, char *new,
-			    int mem_rsv_size,
-			    int struct_size,
-			    int strings_size)
+static int fdt_packblocks_(const char *old, char *new,
+			   int mem_rsv_size,
+			   int struct_size,
+			   int strings_size)
 {
 	int mem_rsv_off, struct_off, strings_off;
 
 	mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);
 	struct_off = mem_rsv_off + mem_rsv_size;
+	if (!can_assume(VALID_DTB) && struct_off < 0)
+		return -FDT_ERR_NOSPACE;
 	strings_off = struct_off + struct_size;
+	if (!can_assume(VALID_DTB) && strings_off < 0)
+		return -FDT_ERR_NOSPACE;
 
 	memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);
 	fdt_set_off_mem_rsvmap(new, mem_rsv_off);
@@ -414,6 +418,8 @@ static void fdt_packblocks_(const char *old, char *new,
 	memmove(new + strings_off, old + fdt_off_dt_strings(old), strings_size);
 	fdt_set_off_dt_strings(new, strings_off);
 	fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
+
+	return 0;
 }
 
 int fdt_open_into(const void *fdt, void *buf, int bufsize)
@@ -461,10 +467,16 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize)
 		return 0;
 	}
 
-	/* Need to reorder */
-	newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
-		+ struct_size + fdt_size_dt_strings(fdt);
-
+	/* Need to reorder. Protect against integer overflows. */
+	newsize = mem_rsv_size + struct_size;
+	if (!can_assume(VALID_DTB) && newsize < 0)
+		return -FDT_ERR_NOSPACE;
+	newsize += fdt_size_dt_strings(fdt);
+	if (!can_assume(VALID_DTB) && newsize < 0)
+		return -FDT_ERR_NOSPACE;
+	newsize += FDT_ALIGN(sizeof(struct fdt_header), 8);
+	if (!can_assume(VALID_DTB) && newsize < 0)
+		return -FDT_ERR_NOSPACE;
 	if (bufsize < newsize)
 		return -FDT_ERR_NOSPACE;
 
@@ -478,8 +490,10 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize)
 			return -FDT_ERR_NOSPACE;
 	}
 
-	fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size,
-			fdt_size_dt_strings(fdt));
+	err = fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size,
+			      fdt_size_dt_strings(fdt));
+	if (err)
+		return err;
 	memmove(buf, tmp, newsize);
 
 	fdt_set_magic(buf, FDT_MAGIC);
@@ -493,7 +507,7 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize)
 
 int fdt_pack(void *fdt)
 {
-	int mem_rsv_size;
+	int mem_rsv_size, err;
 
 	FDT_RW_PROBE(fdt);
 
@@ -505,8 +519,10 @@ int fdt_pack(void *fdt)
 	if (!can_assume(VALID_DTB) && mem_rsv_size < 0)
 		return -FDT_ERR_NOSPACE;
 
-	fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt),
-			fdt_size_dt_strings(fdt));
+	err = fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt),
+			      fdt_size_dt_strings(fdt));
+	if (err)
+		return err;
 	fdt_set_totalsize(fdt, fdt_data_size_(fdt));
 
 	return 0;
-- 
2.17.5


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

* Re: [PATCH 1/4] libfdt: Check for invalid memreserve block
       [not found]     ` <20210611115823.31583-2-andre.przywara-5wv7dgnIgG8@public.gmane.org>
@ 2021-06-12  2:25       ` David Gibson
  0 siblings, 0 replies; 9+ messages in thread
From: David Gibson @ 2021-06-12  2:25 UTC (permalink / raw)
  To: Andre Przywara
  Cc: devicetree-compiler-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Simon Glass

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

On Fri, Jun 11, 2021 at 12:58:20PM +0100, Andre Przywara wrote:
> fdt_num_mem_rsv() can return a negative error value, when the memreserve
> block is not properly terminated or is exceeding its advertised size,
> This can happen on malformed DTBs, and FDT_RO_PROBE() does not cover this.
> 
> Check the return value of fdt_num_mem_rsv() first for an error
> condition, and also check if the total size of the memreserve block
> would provoke a signed integer overflow.
> 
> This fixes issues with malformed DT blobs, where we would end up with
> negative offsets into buffers.
> 
> Signed-off-by: Andre Przywara <andre.przywara-5wv7dgnIgG8@public.gmane.org>
> ---
>  libfdt/fdt_rw.c | 19 +++++++++++++++----
>  1 file changed, 15 insertions(+), 4 deletions(-)
> 
> diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c
> index 3621d36..a0f03d0 100644
> --- a/libfdt/fdt_rw.c
> +++ b/libfdt/fdt_rw.c
> @@ -427,8 +427,13 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize)
>  
>  	FDT_RO_PROBE(fdt);
>  
> -	mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
> -		* sizeof(struct fdt_reserve_entry);
> +	mem_rsv_size = fdt_num_mem_rsv(fdt);
> +	if (mem_rsv_size < 0)

You want a can_assume(VALID_DTB) on this test as well.  The people
doing fdt on miniscule first stage roms really hate every extra
instruction.

> +		return mem_rsv_size;
> +
> +	mem_rsv_size = (mem_rsv_size + 1) * sizeof(struct fdt_reserve_entry);
> +	if (!can_assume(VALID_DTB) && mem_rsv_size < 0)
> +		return -FDT_ERR_NOSPACE;

This second test is not necessary.  fdt_num_mem_rsv() implicitly
checks for a minimally sane mem reserve block, which implies its size
lies within totalsize, which in turn is <= INT_MAX.

If it were necessary, I don't think it's quite safe, because signed
overflow is UB, IIUC.

>  	if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
>  		struct_size = fdt_size_dt_struct(fdt);
> @@ -490,8 +495,14 @@ int fdt_pack(void *fdt)
>  
>  	FDT_RW_PROBE(fdt);
>  
> -	mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
> -		* sizeof(struct fdt_reserve_entry);
> +	mem_rsv_size = fdt_num_mem_rsv(fdt);
> +	if (mem_rsv_size < 0)
> +		return mem_rsv_size;
> +
> +	mem_rsv_size = (mem_rsv_size + 1) * sizeof(struct fdt_reserve_entry);
> +	if (!can_assume(VALID_DTB) && mem_rsv_size < 0)
> +		return -FDT_ERR_NOSPACE;

Same comments here.

>  	fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt),
>  			fdt_size_dt_strings(fdt));
>  	fdt_set_totalsize(fdt, fdt_data_size_(fdt));

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

* Re: [PATCH 2/4] tests: Enhance truncated_memrsv to test fdt_open_into()
       [not found]     ` <20210611115823.31583-3-andre.przywara-5wv7dgnIgG8@public.gmane.org>
@ 2021-06-12  2:26       ` David Gibson
  0 siblings, 0 replies; 9+ messages in thread
From: David Gibson @ 2021-06-12  2:26 UTC (permalink / raw)
  To: Andre Przywara
  Cc: devicetree-compiler-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Simon Glass

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

On Fri, Jun 11, 2021 at 12:58:21PM +0100, Andre Przywara wrote:
> We already check that libfdt's r/o functions can deal properly with
> malformed blobs, where the memreserve block is not correct.
> 
> Add a test for fdt_open_into(), which also queries the memreserve block,
> and uses the return value as an offset into a buffer.
> 
> Signed-off-by: Andre Przywara <andre.przywara-5wv7dgnIgG8@public.gmane.org>

Reviewed-by: David Gibson <david-xT8FGy+AXnRB3Ne2BGzF6laj5H9X9Tb+@public.gmane.org>

> ---
>  tests/truncated_memrsv.c | 10 +++++++++-
>  1 file changed, 9 insertions(+), 1 deletion(-)
> 
> diff --git a/tests/truncated_memrsv.c b/tests/truncated_memrsv.c
> index d78036c..9defef3 100644
> --- a/tests/truncated_memrsv.c
> +++ b/tests/truncated_memrsv.c
> @@ -18,7 +18,8 @@
>  int main(int argc, char *argv[])
>  {
>  	void *fdt = &truncated_memrsv;
> -	int err;
> +	char *buf;
> +	int err, bufsize;
>  	uint64_t addr, size;
>  
>  	test_init(argc, argv);
> @@ -46,5 +47,12 @@ int main(int argc, char *argv[])
>  		FAIL("fdt_get_mem_rsv(1) returned %d instead of -FDT_ERR_BADOFFSET",
>  		     err);
>  
> +	bufsize = fdt_totalsize(fdt);
> +	buf = xmalloc(bufsize);
> +	err = fdt_open_into(fdt, buf, bufsize);
> +	if (err != -FDT_ERR_TRUNCATED)
> +		FAIL("fdt_open_into() returned %d instead of -FDT_ERR_TRUNCATED",
> +		     err);
> +
>  	PASS();
>  }

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

* Re: [PATCH 3/4] libfdt: Check DT struct size for integer overflow
       [not found]     ` <20210611115823.31583-4-andre.przywara-5wv7dgnIgG8@public.gmane.org>
@ 2021-06-12  2:29       ` David Gibson
  0 siblings, 0 replies; 9+ messages in thread
From: David Gibson @ 2021-06-12  2:29 UTC (permalink / raw)
  To: Andre Przywara
  Cc: devicetree-compiler-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Simon Glass

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

On Fri, Jun 11, 2021 at 12:58:22PM +0100, Andre Przywara wrote:
> The DTB header fields store unsigned values for size and offset, however
> we have a 2 GB limit on the overall size, since we use signed "node"
> offsets everywhere.
> 
> As fdt_open_into() is no exception here, check that the advertised DT
> structure size fits in an int, before using that value as an offset into
> a buffer.
> 
> Signed-off-by: Andre Przywara <andre.przywara-5wv7dgnIgG8@public.gmane.org>

Hm, I feel like we should probably just call fdt_check_header() from
fdt_open_into() which will make this check, amongst others.  In fact
slightly tigher, it will check that struct_end <= totalsize <= INT_MAX.

> ---
>  libfdt/fdt_rw.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c
> index a0f03d0..062edcc 100644
> --- a/libfdt/fdt_rw.c
> +++ b/libfdt/fdt_rw.c
> @@ -437,6 +437,8 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize)
>  
>  	if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
>  		struct_size = fdt_size_dt_struct(fdt);
> +		if (!can_assume(VALID_DTB) && struct_size < 0)
> +			return -FDT_ERR_NOSPACE;
>  	} else if (fdt_version(fdt) == 16) {
>  		struct_size = 0;
>  		while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_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] 9+ messages in thread

* Re: [PATCH 4/4] libfdt: Protect buffer size checks against integer overflow
       [not found]     ` <20210611115823.31583-5-andre.przywara-5wv7dgnIgG8@public.gmane.org>
@ 2021-06-15  0:42       ` David Gibson
  0 siblings, 0 replies; 9+ messages in thread
From: David Gibson @ 2021-06-15  0:42 UTC (permalink / raw)
  To: Andre Przywara
  Cc: devicetree-compiler-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Simon Glass

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

On Fri, Jun 11, 2021 at 12:58:23PM +0100, Andre Przywara wrote:
> In fdt_open_into() and fdt_packblocks_(), and in most other libfdt
> functions for that matter, we use signed values for blob offsets and
> sizes. When adding several signed values together, we should check for
> integer overflows, as several valid sizes in inself can easily exceed
> INT_MAX when summed up.
> 
> This is particularly important here, as we use the sum to check against
> the maximum buffer size, or use them directly as buffer offsets.
> Any negative values would not end up well.
> 
> Check for negative values after adding each size value separately. When
> VALID_DTB is defined, the compiler should optimise this to the former
> joint addition, so it should not increase the code size in this case.
> 
> Signed-off-by: Andre Przywara <andre.przywara-5wv7dgnIgG8@public.gmane.org>
> ---
>  libfdt/fdt_rw.c | 42 +++++++++++++++++++++++++++++-------------
>  1 file changed, 29 insertions(+), 13 deletions(-)
> 
> diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c
> index 062edcc..24aff27 100644
> --- a/libfdt/fdt_rw.c
> +++ b/libfdt/fdt_rw.c
> @@ -393,16 +393,20 @@ int fdt_del_node(void *fdt, int nodeoffset)
>  				  endoffset - nodeoffset, 0);
>  }
>  
> -static void fdt_packblocks_(const char *old, char *new,
> -			    int mem_rsv_size,
> -			    int struct_size,
> -			    int strings_size)
> +static int fdt_packblocks_(const char *old, char *new,
> +			   int mem_rsv_size,
> +			   int struct_size,
> +			   int strings_size)
>  {
>  	int mem_rsv_off, struct_off, strings_off;
>  
>  	mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);
>  	struct_off = mem_rsv_off + mem_rsv_size;
> +	if (!can_assume(VALID_DTB) && struct_off < 0)
> +		return -FDT_ERR_NOSPACE;
>  	strings_off = struct_off + struct_size;
> +	if (!can_assume(VALID_DTB) && strings_off < 0)
> +		return -FDT_ERR_NOSPACE;

Hmm... I feel like it would be simpler to call fdt_check_header()
before this, which does equivalent (or stronger) checks.

Also, again signed overflow is UB, so this check isn't quite right.

>  	memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);
>  	fdt_set_off_mem_rsvmap(new, mem_rsv_off);
> @@ -414,6 +418,8 @@ static void fdt_packblocks_(const char *old, char *new,
>  	memmove(new + strings_off, old + fdt_off_dt_strings(old), strings_size);
>  	fdt_set_off_dt_strings(new, strings_off);
>  	fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
> +
> +	return 0;
>  }
>  
>  int fdt_open_into(const void *fdt, void *buf, int bufsize)
> @@ -461,10 +467,16 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize)
>  		return 0;
>  	}
>  
> -	/* Need to reorder */
> -	newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
> -		+ struct_size + fdt_size_dt_strings(fdt);
> -
> +	/* Need to reorder. Protect against integer overflows. */
> +	newsize = mem_rsv_size + struct_size;
> +	if (!can_assume(VALID_DTB) && newsize < 0)
> +		return -FDT_ERR_NOSPACE;
> +	newsize += fdt_size_dt_strings(fdt);
> +	if (!can_assume(VALID_DTB) && newsize < 0)
> +		return -FDT_ERR_NOSPACE;
> +	newsize += FDT_ALIGN(sizeof(struct fdt_header), 8);
> +	if (!can_assume(VALID_DTB) && newsize < 0)
> +		return -FDT_ERR_NOSPACE;
>  	if (bufsize < newsize)
>  		return -FDT_ERR_NOSPACE;
>  
> @@ -478,8 +490,10 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize)
>  			return -FDT_ERR_NOSPACE;
>  	}
>  
> -	fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size,
> -			fdt_size_dt_strings(fdt));
> +	err = fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size,
> +			      fdt_size_dt_strings(fdt));
> +	if (err)
> +		return err;
>  	memmove(buf, tmp, newsize);
>  
>  	fdt_set_magic(buf, FDT_MAGIC);
> @@ -493,7 +507,7 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize)
>  
>  int fdt_pack(void *fdt)
>  {
> -	int mem_rsv_size;
> +	int mem_rsv_size, err;
>  
>  	FDT_RW_PROBE(fdt);
>  
> @@ -505,8 +519,10 @@ int fdt_pack(void *fdt)
>  	if (!can_assume(VALID_DTB) && mem_rsv_size < 0)
>  		return -FDT_ERR_NOSPACE;
>  
> -	fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt),
> -			fdt_size_dt_strings(fdt));
> +	err = fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt),
> +			      fdt_size_dt_strings(fdt));
> +	if (err)
> +		return err;
>  	fdt_set_totalsize(fdt, fdt_data_size_(fdt));
>  
>  	return 0;

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

end of thread, other threads:[~2021-06-15  0:42 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-11 11:58 [PATCH 0/4] libfdt: Check for integer overflows Andre Przywara
     [not found] ` <20210611115823.31583-1-andre.przywara-5wv7dgnIgG8@public.gmane.org>
2021-06-11 11:58   ` [PATCH 1/4] libfdt: Check for invalid memreserve block Andre Przywara
     [not found]     ` <20210611115823.31583-2-andre.przywara-5wv7dgnIgG8@public.gmane.org>
2021-06-12  2:25       ` David Gibson
2021-06-11 11:58   ` [PATCH 2/4] tests: Enhance truncated_memrsv to test fdt_open_into() Andre Przywara
     [not found]     ` <20210611115823.31583-3-andre.przywara-5wv7dgnIgG8@public.gmane.org>
2021-06-12  2:26       ` David Gibson
2021-06-11 11:58   ` [PATCH 3/4] libfdt: Check DT struct size for integer overflow Andre Przywara
     [not found]     ` <20210611115823.31583-4-andre.przywara-5wv7dgnIgG8@public.gmane.org>
2021-06-12  2:29       ` David Gibson
2021-06-11 11:58   ` [PATCH 4/4] libfdt: Protect buffer size checks against " Andre Przywara
     [not found]     ` <20210611115823.31583-5-andre.przywara-5wv7dgnIgG8@public.gmane.org>
2021-06-15  0:42       ` 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.