Skip to contentSkip to conversation

Fix CVE-2018-18440 and CVE-2018-18439

PATCH Superseded Simon Goldschmidt 3 participants 17 messages Dec 14, 2018 – Jan 14, 2019

Subsystems (3)

v6 of 10 Superseded Latest is v10 →

lore
Share & tools
lore
#0 lore raw

v6 cover Fix CVE-2018-18440 and CVE-2018-18439

This series fixes CVE-2018-18440 ("insufficient boundary checks in filesystem image load") by adding restrictions to the 'load' command and fixes CVE-2018-18439 ("insufficient boundary checks in network image boot") by adding restrictions to the tftp code. The functions from lmb.c are used to setup regions of allowed and reserved memory. Then, the file size to load is checked against these addresses and loading the file is aborted if it would overwrite reserved memory.

The memory reservation code is reused from bootm/image.

Changes in v6:
- fix size of allocated regions that need alignment padding
- fix compiling without OF_CONTROL
- fixed NULL pointer access in 'fdt_blob' passed to 'boot_fdt_add_mem_rsv_regions'

Changes in v5:
- added tests for lib/lmb.c
- fixed bug in lmb.c when ram is at the end of 32-bit address range
- fixed a bug in lmb_alloc_addr when resulting reserved ranges get combined

Changes in v4:
- fixed invalid 'if' statement without braces in boot_fdt_reserve_region
- removed patch 7 ("net: remove CONFIG_MCAST_TFTP), adapted patch 8

Changes in v3:
- No patch changes, but needed to resend since patman added too many cc addresses that gmail seemed to detect as spam :-(

Changes in v2:
- added code to reserve devicetree reserved-memory in lmb
- added tftp fixes (patches 7 and 8)
- fixed a bug in new function lmb_alloc_addr

Simon Goldschmidt (9):
test: add test for lib/lmb.c
lmb: fix allocation at end of address range
lib: lmb: reserving overlapping regions should fail
fdt: parse "reserved-memory" for memory reservation
lib: lmb: extend lmb for checks at load time
fs: prevent overwriting reserved memory
bootm: use new common function lmb_init_and_reserve
lmb: remove unused extern declaration
tftp: prevent overwriting reserved memory

 common/bootm.c     |   8 +-
 common/image-fdt.c |  53 +++-
 fs/fs.c            |  56 ++++-
 include/lmb.h      |   7 +-
 lib/Makefile       |   1 +
 lib/lmb.c          | 106 ++++++--
 net/tftp.c         |  66 ++++-
 test/lib/Makefile  |   1 +
 test/lib/lmb.c     | 600 +++++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 846 insertions(+), 52 deletions(-)
 create mode 100644 test/lib/lmb.c
Signature · 1 line
2.17.1
#1 lore raw

PATCH v6 1/9 test: add test for lib/lmb.c

Add basic tests for the lmb memory allocation code used to reserve and allocate memory during boot.

---

Changes in v6: None
Changes in v5:
- this patch is new in v5

Changes in v4: None
Changes in v2: None

 test/lib/Makefile |   1 +
 test/lib/lmb.c    | 297 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 298 insertions(+)
 create mode 100644 test/lib/lmb.c
test/lib/Makefile +1 -0
index ea68fae566..5a636aac74 100644
@@ -3,3 +3,4 @@
 # (C) Copyright 2018
 # Mario Six, Guntermann & Drunck GmbH, mario.six at gdsys.cc
 obj-y += hexdump.o
+obj-y += lmb.o
test/lib/lmb.c new file +297 -0

297 lines not on this page · raw message

Signature · 1 line
2.17.1
#2 lore raw

PATCH v6 1/9 test: add test for lib/lmb.c

Hi Simon,

Quoted 11 lines · Simon Goldschmidt

Add basic tests for the lmb memory allocation code used to reserve and allocate memory during boot.

---

Changes in v6: None
Changes in v5:
- this patch is new in v5

Quoted 7 lines

Changes in v4: None
Changes in v2: None

 test/lib/Makefile |   1 +
 test/lib/lmb.c    | 297 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 298 insertions(+)
 create mode 100644 test/lib/lmb.c

Seems fine. I wonder if it would be easier to simulate a 10-byte memory size? It shouldn't matter how big it is.

Regards,
Simon

#3 lore raw

PATCH v6 1/9 test: add test for lib/lmb.c

Am 05.01.2019 um 02:56 schrieb Simon Glass:

Quoted 23 lines

Hi Simon,

Quoted 11 lines · Simon Goldschmidt

Add basic tests for the lmb memory allocation code used to reserve and allocate memory during boot.

---

Changes in v6: None
Changes in v5:
- this patch is new in v5

Quoted 7 lines

Changes in v4: None
Changes in v2: None

  test/lib/Makefile |   1 +
  test/lib/lmb.c    | 297 ++++++++++++++++++++++++++++++++++++++++++++++
  2 files changed, 298 insertions(+)

create mode 100644 test/lib/lmb.c

Quoted 4 lines

Seems fine. I wonder if it would be easier to simulate a 10-byte memory size? It shouldn't matter how big it is.

This one slipped through somehow, sorry.

I wrote the test by simulating real values. Since there is no memory involved, only numbers, personally, I don't think simulating 10 bytes makes it easier than simulating 512 MiB. I'd leave it like it is, unless you insist...

Regards,
Simon

#4 lore raw

PATCH v6 2/9 lmb: fix allocation at end of address range

In reply to Simon Goldschmidt

The lmb code fails if base + size of RAM overflows to zero.

Fix this by calculating end as 'base + size - 1' instead of 'base + size' where appropriate.

Added tests to assert this is fixed.

---

Changes in v6: None
Changes in v5:
- this patch is new in v5

Changes in v4: None
Changes in v2: None

 lib/lmb.c      | 29 ++++++++++++-----------------
 test/lib/lmb.c | 29 ++++++++++++++++++++++++++---
 2 files changed, 38 insertions(+), 20 deletions(-)
lib/lmb.c +12 -17
index 1705417348..6d3dcf4e09 100644
@@ -43,7 +43,10 @@ void lmb_dump_all(struct lmb *lmb)
 static long lmb_addrs_overlap(phys_addr_t base1,
 		phys_size_t size1, phys_addr_t base2, phys_size_t size2)
 {
-	return ((base1 < (base2+size2)) && (base2 < (base1+size1)));
+	const phys_addr_t base1_end = base1 + size1 - 1;
+	const phys_addr_t base2_end = base2 + size2 - 1;
+
+	return ((base1 <= base2_end) && (base2 <= base1_end));
 }
 
 static long lmb_addrs_adjacent(phys_addr_t base1, phys_size_t size1,
@@ -89,18 +92,9 @@ static void lmb_coalesce_regions(struct lmb_region *rgn,
 
 void lmb_init(struct lmb *lmb)
 {
-	/* Create a dummy zero size LMB which will get coalesced away later.
-	 * This simplifies the lmb_add() code below...
-	 */
-	lmb->memory.region[0].base = 0;
-	lmb->memory.region[0].size = 0;
-	lmb->memory.cnt = 1;
+	lmb->memory.cnt = 0;
 	lmb->memory.size = 0;
-
-	/* Ditto. */
-	lmb->reserved.region[0].base = 0;
-	lmb->reserved.region[0].size = 0;
-	lmb->reserved.cnt = 1;
+	lmb->reserved.cnt = 0;
 	lmb->reserved.size = 0;
 }
 
@@ -110,9 +104,10 @@ static long lmb_add_region(struct lmb_region *rgn, phys_addr_t base, phys_size_t
 	unsigned long coalesced = 0;
 	long adjacent, i;
 
-	if ((rgn->cnt == 1) && (rgn->region[0].size == 0)) {
+	if (rgn->cnt == 0) {
 		rgn->region[0].base = base;
 		rgn->region[0].size = size;
+		rgn->cnt = 1;
 		return 0;
 	}
 
@@ -183,7 +178,7 @@ long lmb_free(struct lmb *lmb, phys_addr_t base, phys_size_t size)
 {
 	struct lmb_region *rgn = &(lmb->reserved);
 	phys_addr_t rgnbegin, rgnend;
-	phys_addr_t end = base + size;
+	phys_addr_t end = base + size - 1;
 	int i;
 
 	rgnbegin = rgnend = 0; /* supress gcc warnings */
@@ -191,7 +186,7 @@ long lmb_free(struct lmb *lmb, phys_addr_t base, phys_size_t size)
 	/* Find the region where (base, size) belongs to */
 	for (i=0; i < rgn->cnt; i++) {
 		rgnbegin = rgn->region[i].base;
-		rgnend = rgnbegin + rgn->region[i].size;
+		rgnend = rgnbegin + rgn->region[i].size - 1;
 
 		if ((rgnbegin <= base) && (end <= rgnend))
 			break;
@@ -209,7 +204,7 @@ long lmb_free(struct lmb *lmb, phys_addr_t base, phys_size_t size)
 
 	/* Check to see if region is matching@the front */
 	if (rgnbegin == base) {
-		rgn->region[i].base = end;
+		rgn->region[i].base = end + 1;
 		rgn->region[i].size -= size;
 		return 0;
 	}
@@ -225,7 +220,7 @@ long lmb_free(struct lmb *lmb, phys_addr_t base, phys_size_t size)
 	 * beginging of the hole and add the region after hole.
 	 */
 	rgn->region[i].size = base - rgn->region[i].base;
-	return lmb_add_region(rgn, end, rgnend - end);
+	return lmb_add_region(rgn, end + 1, rgnend - end);
 }
 
 long lmb_reserve(struct lmb *lmb, phys_addr_t base, phys_size_t size)
test/lib/lmb.c +26 -3
index dd7ba14b34..fb7ca45ef1 100644
@@ -146,8 +146,15 @@ static int test_multi_alloc_512mb(struct unit_test_state *uts,
 /* Create a memory region with one reserved region and allocate */
 static int lib_test_lmb_simple(struct unit_test_state *uts)
 {
+	int ret;
+
 	/* simulate 512 MiB RAM beginning at 1GiB */
-	return test_multi_alloc_512mb(uts, 0x40000000);
+	ret = test_multi_alloc_512mb(uts, 0x40000000);
+	if (ret)
+		return ret;
+
+	/* simulate 512 MiB RAM beginning at 1.5GiB */
+	return test_multi_alloc_512mb(uts, 0xE0000000);
 }
 
 DM_TEST(lib_test_lmb_simple, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
@@ -206,7 +213,15 @@ static int test_bigblock(struct unit_test_state *uts, const phys_addr_t ram)
 
 static int lib_test_lmb_big(struct unit_test_state *uts)
 {
-	return test_bigblock(uts, 0x40000000);
+	int ret;
+
+	/* simulate 512 MiB RAM beginning at 1GiB */
+	ret = test_bigblock(uts, 0x40000000);
+	if (ret)
+		return ret;
+
+	/* simulate 512 MiB RAM beginning at 1.5GiB */
+	return test_bigblock(uts, 0xE0000000);
 }
 
 DM_TEST(lib_test_lmb_big, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
@@ -247,7 +262,15 @@ static int test_noreserved(struct unit_test_state *uts, const phys_addr_t ram)
 
 static int lib_test_lmb_noreserved(struct unit_test_state *uts)
 {
-	return test_noreserved(uts, 0x40000000);
+	int ret;
+
+	/* simulate 512 MiB RAM beginning at 1GiB */
+	ret = test_noreserved(uts, 0x40000000);
+	if (ret)
+		return ret;
+
+	/* simulate 512 MiB RAM beginning at 1.5GiB */
+	return test_noreserved(uts, 0xE0000000);
 }
 
 DM_TEST(lib_test_lmb_noreserved, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
Signature · 1 line
2.17.1
#5 lore raw

PATCH v6 3/9 lib: lmb: reserving overlapping regions should fail

In reply to Simon Goldschmidt

lmb_add_region handles overlapping regions wrong: instead of merging or rejecting to add a new reserved region that overlaps an existing one, it just adds the new region.

Since internally the same function is used for lmb_alloc, change lmb_add_region to reject overlapping regions.

Also, to keep reserved memory correct after 'free', reserved entries created by allocating memory must not set their size to a multiple of alignment but to the original size. This ensures the reserved region is completely removed when the caller calls 'lmb_free', as this one takes the same size as passed to 'lmb_alloc' etc.

Add test to assert this.

---

Changes in v6:
- fix size of allocated regions that need alignment padding

Changes in v5:
- added a test for this bug

Changes in v4: None
Changes in v2: None

 lib/lmb.c      | 11 +++---
 test/lib/lmb.c | 94 +++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 90 insertions(+), 15 deletions(-)
lib/lmb.c +4 -7
index 6d3dcf4e09..cd297f8202 100644
@@ -131,6 +131,9 @@ static long lmb_add_region(struct lmb_region *rgn, phys_addr_t base, phys_size_t
 			rgn->region[i].size += size;
 			coalesced++;
 			break;
+		} else if (lmb_addrs_overlap(base, size, rgnbase, rgnsize)) {
+			/* regions overlap */
+			return -1;
 		}
 	}
 
@@ -269,11 +272,6 @@ static phys_addr_t lmb_align_down(phys_addr_t addr, phys_size_t size)
 	return addr & ~(size - 1);
 }
 
-static phys_addr_t lmb_align_up(phys_addr_t addr, ulong size)
-{
-	return (addr + (size - 1)) & ~(size - 1);
-}
-
 phys_addr_t __lmb_alloc_base(struct lmb *lmb, phys_size_t size, ulong align, phys_addr_t max_addr)
 {
 	long i, j;
@@ -302,8 +300,7 @@ phys_addr_t __lmb_alloc_base(struct lmb *lmb, phys_size_t size, ulong align, phy
 			if (j < 0) {
 				/* This area isn't reserved, take it */
 				if (lmb_add_region(&lmb->reserved, base,
-							lmb_align_up(size,
-								align)) < 0)
+						   size) < 0)
 					return 0;
 				return base;
 			}
test/lib/lmb.c +86 -8

138 lines not on this page · raw message

Signature · 1 line
2.17.1
#6 lore raw

PATCH v6 3/9 lib: lmb: reserving overlapping regions should fail

Quoted 16 lines · Simon Goldschmidt

lmb_add_region handles overlapping regions wrong: instead of merging or rejecting to add a new reserved region that overlaps an existing one, it just adds the new region.

Since internally the same function is used for lmb_alloc, change lmb_add_region to reject overlapping regions.

Also, to keep reserved memory correct after 'free', reserved entries created by allocating memory must not set their size to a multiple of alignment but to the original size. This ensures the reserved region is completely removed when the caller calls 'lmb_free', as this one takes the same size as passed to 'lmb_alloc' etc.

Add test to assert this.

[snip]

test/lib/lmb.c static int test_noreserved(struct unit_test_state *uts, const phys_addr_t ram) · line 265

Quoted 8 lines
test/lib/lmb.c +8 -0
+	if (alloc_size == alloc_size_aligned)
+		ASSERT_LMB(&lmb, ram, ram_size, 1, ram + ram_size -
+			   (alloc_size_aligned * 2), alloc_size * 2, 0, 0, 0,
+			   0);
+	else
+		ASSERT_LMB(&lmb, ram, ram_size, 2, ram + ram_size -
+			   (alloc_size_aligned * 2), alloc_size, ram + ram_size
+			   - alloc_size_aligned, alloc_size, 0, 0);

I've fixed this inline for testing but, we need braces with this if/else as I guess due to all of the macro expansion it's more than one line and both gcc and clang fail.

Signature · 8 lines
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20181215/dd46105f/attachment.sig>
#7 lore raw

PATCH v6 4/9 fdt: parse "reserved-memory" for memory reservation

In reply to Simon Goldschmidt

boot_fdt_add_mem_rsv_regions() adds reserved memory sections to an lmb struct. Currently, it only parses regions described by /memreserve/ entries.

Extend this to the more commonly used scheme of the "reserved-memory" node.

---

Changes in v6:
- fix compiling without OF_CONTROL

Changes in v5: None
Changes in v4:
- fixed invalid 'if' statement without braces in boot_fdt_reserve_region

Changes in v2:
- this patch is new in v2

 common/image-fdt.c | 53 +++++++++++++++++++++++++++++++++++++++-------
 lib/Makefile       |  1 +
 2 files changed, 46 insertions(+), 8 deletions(-)
common/image-fdt.c +45 -8
index 95748f0ae1..5c0d6db3fe 100644
@@ -10,6 +10,7 @@
 
 #include <common.h>
 #include <fdt_support.h>
+#include <fdtdec.h>
 #include <errno.h>
 #include <image.h>
 #include <linux/libfdt.h>
@@ -67,30 +68,66 @@ static const image_header_t *image_get_fdt(ulong fdt_addr)
 }
 #endif
 
+static void boot_fdt_reserve_region(struct lmb *lmb, uint64_t addr,
+				    uint64_t size)
+{
+	int ret;
+
+	ret = lmb_reserve(lmb, addr, size);
+	if (!ret) {
+		debug("   reserving fdt memory region: addr=%llx size=%llx\n",
+		      (unsigned long long)addr, (unsigned long long)size);
+	} else {
+		puts("ERROR: reserving fdt memory region failed ");
+		printf("(addr=%llx size=%llx)\n",
+		       (unsigned long long)addr, (unsigned long long)size);
+	}
+}
+
 /**
- * boot_fdt_add_mem_rsv_regions - Mark the memreserve sections as unusable
+ * boot_fdt_add_mem_rsv_regions - Mark the memreserve and reserved-memory
+ * sections as unusable
  * @lmb: pointer to lmb handle, will be used for memory mgmt
  * @fdt_blob: pointer to fdt blob base address
  *
- * Adds the memreserve regions in the dtb to the lmb block.  Adding the
- * memreserve regions prevents u-boot from using them to store the initrd
- * or the fdt blob.
+ * Adds the and reserved-memorymemreserve regions in the dtb to the lmb block.
+ * Adding the memreserve regions prevents u-boot from using them to store the
+ * initrd or the fdt blob.
  */
 void boot_fdt_add_mem_rsv_regions(struct lmb *lmb, void *fdt_blob)
 {
 	uint64_t addr, size;
-	int i, total;
+	int i, total, ret;
+	int nodeoffset, subnode;
+	struct fdt_resource res;
 
 	if (fdt_check_header(fdt_blob) != 0)
 		return;
 
+	/* process memreserve sections */
 	total = fdt_num_mem_rsv(fdt_blob);
 	for (i = 0; i < total; i++) {
 		if (fdt_get_mem_rsv(fdt_blob, i, &addr, &size) != 0)
 			continue;
-		printf("   reserving fdt memory region: addr=%llx size=%llx\n",
-		       (unsigned long long)addr, (unsigned long long)size);
-		lmb_reserve(lmb, addr, size);
+		boot_fdt_reserve_region(lmb, addr, size);
+	}
+
+	/* process reserved-memory */
+	nodeoffset = fdt_subnode_offset(fdt_blob, 0, "reserved-memory");
+	if (nodeoffset >= 0) {
+		subnode = fdt_first_subnode(fdt_blob, nodeoffset);
+		while (subnode >= 0) {
+			/* check if this subnode has a reg property */
+			ret = fdt_get_resource(fdt_blob, subnode, "reg", 0,
+					       &res);
+			if (!ret) {
+				addr = res.start;
+				size = res.end - res.start + 1;
+				boot_fdt_reserve_region(lmb, addr, size);
+			}
+
+			subnode = fdt_next_subnode(fdt_blob, subnode);
+		}
 	}
 }
 
lib/Makefile +1 -0
index a6dd928a92..1972fe8cf1 100644
@@ -30,6 +30,7 @@ obj-y += crc7.o
 obj-y += crc8.o
 obj-y += crc16.o
 obj-$(CONFIG_ERRNO_STR) += errno_str.o
+obj-$(CONFIG_FIT) += fdtdec.o
 obj-$(CONFIG_FIT) += fdtdec_common.o
 obj-$(CONFIG_TEST_FDTDEC) += fdtdec_test.o
 obj-$(CONFIG_GZIP_COMPRESSED) += gzip.o
Signature · 1 line
2.17.1
#8 lore raw

PATCH v6 4/9 fdt: parse "reserved-memory" for memory reservation

Quoted 18 lines · Simon Goldschmidt

boot_fdt_add_mem_rsv_regions() adds reserved memory sections to an lmb struct. Currently, it only parses regions described by /memreserve/ entries.

Extend this to the more commonly used scheme of the "reserved-memory" node.

---

Changes in v6:
- fix compiling without OF_CONTROL

Changes in v5: None
Changes in v4:
- fixed invalid 'if' statement without braces in boot_fdt_reserve_region

Quoted 7 lines

Changes in v2:
- this patch is new in v2

 common/image-fdt.c | 53 +++++++++++++++++++++++++++++++++++++++-------
 lib/Makefile       |  1 +
 2 files changed, 46 insertions(+), 8 deletions(-)
#9 lore raw

PATCH v6 5/9 lib: lmb: extend lmb for checks at load time

In reply to Simon Goldschmidt

This adds two new functions, lmb_alloc_addr and
lmb_get_unreserved_size.

lmb_alloc_addr behaves like lmb_alloc, but it tries to allocate a pre-specified address range. Unlike lmb_reserve, this address range must be inside one of the memory ranges that has been set up with lmb_add.

lmb_get_unreserved_size returns the number of bytes that can be used up to the next reserved region or the end of valid ram. This can be 0 if the address passed is reserved.

Added test for these new functions.

---

Changes in v6: None
Changes in v5:
- fixed lmb_alloc_addr when resulting reserved ranges get combined
- added test for these new functions

Changes in v4: None
Changes in v2:
- added lmb_get_unreserved_size() for tftp

 include/lmb.h  |   3 +
 lib/lmb.c      |  53 +++++++++++++
 test/lib/lmb.c | 202 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 258 insertions(+)
include/lmb.h +3 -0
index f04d058093..7d7e2a78dc 100644
@@ -38,6 +38,9 @@ extern phys_addr_t lmb_alloc_base(struct lmb *lmb, phys_size_t size, ulong align
 			    phys_addr_t max_addr);
 extern phys_addr_t __lmb_alloc_base(struct lmb *lmb, phys_size_t size, ulong align,
 			      phys_addr_t max_addr);
+extern phys_addr_t lmb_alloc_addr(struct lmb *lmb, phys_addr_t base,
+				  phys_size_t size);
+extern phys_size_t lmb_get_unreserved_size(struct lmb *lmb, phys_addr_t addr);
 extern int lmb_is_reserved(struct lmb *lmb, phys_addr_t addr);
 extern long lmb_free(struct lmb *lmb, phys_addr_t base, phys_size_t size);
 
lib/lmb.c +53 -0

59 lines not on this page · raw message

test/lib/lmb.c +202 -0

205 lines not on this page · raw message

Signature · 1 line
2.17.1
#10 lore raw

PATCH v6 6/9 fs: prevent overwriting reserved memory

In reply to Simon Goldschmidt

This fixes CVE-2018-18440 ("insufficient boundary checks in filesystem image load") by using lmb to check the load size of a file against reserved memory addresses.

---

Changes in v6:
- fixed NULL pointer access in 'fdt_blob' passed to 'boot_fdt_add_mem_rsv_regions'

Changes in v5: None
Changes in v4: None
Changes in v2: None

 fs/fs.c       | 56 ++++++++++++++++++++++++++++++++++++++++++++++++---
 include/lmb.h |  2 ++
 lib/lmb.c     | 13 ++++++++++++
 3 files changed, 68 insertions(+), 3 deletions(-)
fs/fs.c +53 -3
index cb265174e2..400aa921a7 100644
@@ -429,13 +429,57 @@ int fs_size(const char *filename, loff_t *size)
 	return ret;
 }
 
-int fs_read(const char *filename, ulong addr, loff_t offset, loff_t len,
-	    loff_t *actread)
+#ifdef CONFIG_LMB
+/* Check if a file may be read to the given address */
+static int fs_read_lmb_check(const char *filename, ulong addr, loff_t offset,
+			     loff_t len, struct fstype_info *info)
+{
+	struct lmb lmb;
+	int ret;
+	loff_t size;
+	loff_t read_len;
+
+	/* get the actual size of the file */
+	ret = info->size(filename, &size);
+	if (ret)
+		return ret;
+	if (offset >= size) {
+		/* offset >= EOF, no bytes will be written */
+		return 0;
+	}
+	read_len = size - offset;
+
+	/* limit to 'len' if it is smaller */
+	if (len && len < read_len)
+		read_len = len;
+
+	lmb_init_and_reserve(&lmb, gd->bd->bi_dram[0].start,
+			     gd->bd->bi_dram[0].size, (void *)gd->fdt_blob);
+	lmb_dump_all(&lmb);
+
+	if (lmb_alloc_addr(&lmb, addr, read_len) == addr)
+		return 0;
+
+	printf("** Reading file would overwrite reserved memory **\n");
+	return -1;
+}
+#endif
+
+static int _fs_read(const char *filename, ulong addr, loff_t offset, loff_t len,
+		    int do_lmb_check, loff_t *actread)
 {
 	struct fstype_info *info = fs_get_info(fs_type);
 	void *buf;
 	int ret;
 
+#ifdef CONFIG_LMB
+	if (do_lmb_check) {
+		ret = fs_read_lmb_check(filename, addr, offset, len, info);
+		if (ret)
+			return ret;
+	}
+#endif
+
 	/*
 	 * We don't actually know how many bytes are being read, since len==0
 	 * means read the whole file.
@@ -452,6 +496,12 @@ int fs_read(const char *filename, ulong addr, loff_t offset, loff_t len,
 	return ret;
 }
 
+int fs_read(const char *filename, ulong addr, loff_t offset, loff_t len,
+	    loff_t *actread)
+{
+	return _fs_read(filename, addr, offset, len, 0, actread);
+}
+
 int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len,
 	     loff_t *actwrite)
 {
@@ -622,7 +672,7 @@ int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
 		pos = 0;
 
 	time = get_timer(0);
-	ret = fs_read(filename, addr, pos, bytes, &len_read);
+	ret = _fs_read(filename, addr, pos, bytes, 1, &len_read);
 	time = get_timer(time);
 	if (ret < 0)
 		return 1;
include/lmb.h +2 -0
index 7d7e2a78dc..62da85e716 100644
@@ -31,6 +31,8 @@ struct lmb {
 extern struct lmb lmb;
 
 extern void lmb_init(struct lmb *lmb);
+extern void lmb_init_and_reserve(struct lmb *lmb, phys_addr_t base,
+				 phys_size_t size, void *fdt_blob);
 extern long lmb_add(struct lmb *lmb, phys_addr_t base, phys_size_t size);
 extern long lmb_reserve(struct lmb *lmb, phys_addr_t base, phys_size_t size);
 extern phys_addr_t lmb_alloc(struct lmb *lmb, phys_size_t size, ulong align);
lib/lmb.c +13 -0
index e380a0a722..3407705fa7 100644
@@ -98,6 +98,19 @@ void lmb_init(struct lmb *lmb)
 	lmb->reserved.size = 0;
 }
 
+/* Initialize the struct, add memory and call arch/board reserve functions */
+void lmb_init_and_reserve(struct lmb *lmb, phys_addr_t base, phys_size_t size,
+			  void *fdt_blob)
+{
+	lmb_init(lmb);
+	lmb_add(lmb, base, size);
+	arch_lmb_reserve(lmb);
+	board_lmb_reserve(lmb);
+
+	if (IMAGE_ENABLE_OF_LIBFDT && fdt_blob)
+		boot_fdt_add_mem_rsv_regions(lmb, fdt_blob);
+}
+
 /* This routine called with relocation disabled. */
 static long lmb_add_region(struct lmb_region *rgn, phys_addr_t base, phys_size_t size)
 {
Signature · 1 line
2.17.1
#11 lore raw

PATCH v6 6/9 fs: prevent overwriting reserved memory

Hi Simon,

Quoted 12 lines · Simon Goldschmidt

This fixes CVE-2018-18440 ("insufficient boundary checks in filesystem image load") by using lmb to check the load size of a file against reserved memory addresses.

---

Changes in v6:
- fixed NULL pointer access in 'fdt_blob' passed to 'boot_fdt_add_mem_rsv_regions'

Quoted 8 lines

Changes in v5: None
Changes in v4: None
Changes in v2: None

 fs/fs.c       | 56 ++++++++++++++++++++++++++++++++++++++++++++++++---
 include/lmb.h |  2 ++
 lib/lmb.c     | 13 ++++++++++++
 3 files changed, 68 insertions(+), 3 deletions(-)

How about -ENOSPC instead of -1?

Regards,
Simon

#12 lore raw

PATCH v6 6/9 fs: prevent overwriting reserved memory

Quoted 21 lines · Simon Glass

Hi Simon,

Quoted 16 lines · Simon Goldschmidt

This fixes CVE-2018-18440 ("insufficient boundary checks in filesystem image load") by using lmb to check the load size of a file against reserved memory addresses.

---

Changes in v6:
- fixed NULL pointer access in 'fdt_blob' passed to 'boot_fdt_add_mem_rsv_regions'

Changes in v5: None
Changes in v4: None
Changes in v2: None

Quoted 8 lines
Quoted 4 lines
 fs/fs.c       | 56 ++++++++++++++++++++++++++++++++++++++++++++++++---
 include/lmb.h |  2 ++
 lib/lmb.c     | 13 ++++++++++++
 3 files changed, 68 insertions(+), 3 deletions(-)

How about -ENOSPC instead of -1?

You mean in fs_read_lmb_check()? That would probably a good idea.

Not that you were replying to an old version, I had sent out v9 on 12/19/2018. There's still -1 in there however. I'll send a v10 that fixes this.

Regards,
Simon

#13 lore raw

PATCH v6 7/9 bootm: use new common function lmb_init_and_reserve

In reply to Simon Goldschmidt

This reduces duplicate code only.

---

Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v2: None

 common/bootm.c | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)
common/bootm.c +2 -6
index 8bf84ebcb7..31e4f0f794 100644
@@ -56,15 +56,11 @@ static void boot_start_lmb(bootm_headers_t *images)
 	ulong		mem_start;
 	phys_size_t	mem_size;
 
-	lmb_init(&images->lmb);
-
 	mem_start = env_get_bootm_low();
 	mem_size = env_get_bootm_size();
 
-	lmb_add(&images->lmb, (phys_addr_t)mem_start, mem_size);
-
-	arch_lmb_reserve(&images->lmb);
-	board_lmb_reserve(&images->lmb);
+	lmb_init_and_reserve(&images->lmb, (phys_addr_t)mem_start, mem_size,
+			     NULL);
 }
 #else
 #define lmb_reserve(lmb, base, size)
Signature · 1 line
2.17.1
#15 lore raw

PATCH v6 8/9 lmb: remove unused extern declaration

In reply to Simon Goldschmidt

lmb.h includes an extern declaration of "struct lmb lmb;" which is not used anywhere, so remove it.

---

Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v2:
- this patch is new in v2

 include/lmb.h | 2 --
 1 file changed, 2 deletions(-)
include/lmb.h +0 -2
index 62da85e716..1bb003e35e 100644
@@ -28,8 +28,6 @@ struct lmb {
 	struct lmb_region reserved;
 };
 
-extern struct lmb lmb;
-
 extern void lmb_init(struct lmb *lmb);
 extern void lmb_init_and_reserve(struct lmb *lmb, phys_addr_t base,
 				 phys_size_t size, void *fdt_blob);
Signature · 1 line
2.17.1
#16 lore raw

PATCH v6 9/9 tftp: prevent overwriting reserved memory

In reply to Simon Goldschmidt

This fixes CVE-2018-18439 ("insufficient boundary checks in network image boot") by using lmb to check for a valid range to store received blocks.

---

Changes in v6: None
Changes in v5: None
Changes in v4:
- this was patch 8, is now patch 7
- lines changed because v3 patch 7 got removed and MCAST_TFTP still exists

Changes in v2:
- this patch is new in v2

 net/tftp.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 57 insertions(+), 9 deletions(-)
net/tftp.c +57 -9
index 68ffd81414..d31364166e 100644
@@ -17,6 +17,8 @@
 #include <flash.h>
 #endif
 
+DECLARE_GLOBAL_DATA_PTR;
+
 /* Well known TFTP port # */
 #define WELL_KNOWN_PORT	69
 /* Millisecs to timeout for lost pkt */
@@ -81,6 +83,8 @@ static ulong	tftp_block_wrap;
 /* memory offset due to wrapping */
 static ulong	tftp_block_wrap_offset;
 static int	tftp_state;
+static ulong	tftp_load_addr;
+static ulong	tftp_load_size;
 #ifdef CONFIG_TFTP_TSIZE
 /* The file size reported by the server */
 static int	tftp_tsize;
@@ -164,10 +168,11 @@ static void mcast_cleanup(void)
 
 #endif	/* CONFIG_MCAST_TFTP */
 
-static inline void store_block(int block, uchar *src, unsigned len)
+static inline int store_block(int block, uchar *src, unsigned int len)
 {
 	ulong offset = block * tftp_block_size + tftp_block_wrap_offset;
 	ulong newsize = offset + len;
+	ulong store_addr = tftp_load_addr + offset;
 #ifdef CONFIG_SYS_DIRECT_FLASH_TFTP
 	int i, rc = 0;
 
@@ -175,24 +180,30 @@ static inline void store_block(int block, uchar *src, unsigned len)
 		/* start address in flash? */
 		if (flash_info[i].flash_id == FLASH_UNKNOWN)
 			continue;
-		if (load_addr + offset >= flash_info[i].start[0]) {
+		if (store_addr >= flash_info[i].start[0]) {
 			rc = 1;
 			break;
 		}
 	}
 
 	if (rc) { /* Flash is destination for this packet */
-		rc = flash_write((char *)src, (ulong)(load_addr+offset), len);
+		rc = flash_write((char *)src, store_addr, len);
 		if (rc) {
 			flash_perror(rc);
-			net_set_state(NETLOOP_FAIL);
-			return;
+			return rc;
 		}
 	} else
 #endif /* CONFIG_SYS_DIRECT_FLASH_TFTP */
 	{
-		void *ptr = map_sysmem(load_addr + offset, len);
+		void *ptr;
 
+		if (store_addr < tftp_load_addr ||
+		    store_addr + len > tftp_load_addr + tftp_load_size) {
+			puts("\nTFTP error: ");
+			puts("trying to overwrite reserved memory...\n");
+			return -1;
+		}
+		ptr = map_sysmem(store_addr, len);
 		memcpy(ptr, src, len);
 		unmap_sysmem(ptr);
 	}
@@ -203,6 +214,8 @@ static inline void store_block(int block, uchar *src, unsigned len)
 
 	if (net_boot_file_size < newsize)
 		net_boot_file_size = newsize;
+
+	return 0;
 }
 
 /* Clear our state ready for a new transfer */
@@ -606,7 +619,11 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
 		timeout_count_max = tftp_timeout_count_max;
 		net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
 
-		store_block(tftp_cur_block - 1, pkt + 2, len);
+		if (store_block(tftp_cur_block - 1, pkt + 2, len)) {
+			eth_halt();
+			net_set_state(NETLOOP_FAIL);
+			break;
+		}
 
 		/*
 		 *	Acknowledge the block just received, which will prompt
@@ -695,6 +712,24 @@ static void tftp_timeout_handler(void)
 	}
 }
 
+/* Initialize tftp_load_addr and tftp_load_size from load_addr and lmb */
+static int tftp_init_load_addr(void)
+{
+	struct lmb lmb;
+	phys_size_t max_size;
+
+	tftp_load_addr = load_addr;
+
+	lmb_init_and_reserve(&lmb, gd->bd->bi_dram[0].start,
+			     gd->bd->bi_dram[0].size, (void *)gd->fdt_blob);
+
+	max_size = lmb_get_unreserved_size(&lmb, tftp_load_addr);
+	if (!max_size)
+		return -1;
+
+	tftp_load_size = max_size;
+	return 0;
+}
 
 void tftp_start(enum proto_t protocol)
 {
@@ -791,7 +826,14 @@ void tftp_start(enum proto_t protocol)
 	} else
 #endif
 	{
-		printf("Load address: 0x%lx\n", load_addr);
+		if (tftp_init_load_addr()) {
+			eth_halt();
+			net_set_state(NETLOOP_FAIL);
+			puts("\nTFTP error: ");
+			puts("trying to overwrite reserved memory...\n");
+			return;
+		}
+		printf("Load address: 0x%lx\n", tftp_load_addr);
 		puts("Loading: *\b");
 		tftp_state = STATE_SEND_RRQ;
 #ifdef CONFIG_CMD_BOOTEFI
@@ -842,9 +884,15 @@ void tftp_start_server(void)
 {
 	tftp_filename[0] = 0;
 
+	if (tftp_init_load_addr()) {
+		eth_halt();
+		net_set_state(NETLOOP_FAIL);
+		puts("\nTFTP error: trying to overwrite reserved memory...\n");
+		return;
+	}
 	printf("Using %s device\n", eth_get_name());
 	printf("Listening for TFTP transfer on %pI4\n", &net_ip);
-	printf("Load address: 0x%lx\n", load_addr);
+	printf("Load address: 0x%lx\n", tftp_load_addr);
 
 	puts("Loading: *\b");
 
Signature · 1 line
2.17.1