summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Gibson <david@gibson.dropbear.id.au>2017-10-23 06:45:56 +0300
committerSebastian Huber <sebastian.huber@embedded-brains.de>2018-07-19 07:01:11 +0200
commit8073f95ef56a7c6dce04c04a12330b6c2839e23c (patch)
tree43d49cae0d12750281feb4c88dca9596b92df46c
parentc4267b1e5f6ef5eb90d7cfdccdec196c9a7d3fec (diff)
downloadrtems-8073f95ef56a7c6dce04c04a12330b6c2839e23c.tar.bz2
libfdt: Make fdt_check_header() more thorough
Currently fdt_check_header() performs only some rudimentary checks, which is not really what the name suggests. This strengthens fdt_check_header() to check as much about the blob as is possible from the header alone: as well as checking the magic number and version, it checks that the total size is sane, and that all the sub-blocks within the blob lie within the total size. * This broadens the meaning of FDT_ERR_TRUNCATED to cover all sorts of improperly terminated blocks as well as just a structure block without FDT_END. * This makes fdt_check_header() only succeed on "complete" blobs, not in-progress sequential write blobs. The only reason this didn't fail before was that this function used to be called by many RO functions which are supposed to also work on incomplete SW blobs. Signed-off-by: David Gibson <david@gibson.dropbear.id.au> Tested-by: Alexey Kardashevskiy <aik@ozlabs.ru> Reviewed-by: Alexey Kardashevskiy <aik@ozlabs.ru> Reviewed-by: Simon Glass <sjg@chromium.org>
-rw-r--r--cpukit/dtc/libfdt/fdt.c57
-rw-r--r--cpukit/include/libfdt.h15
-rw-r--r--cpukit/include/libfdt_env.h1
3 files changed, 66 insertions, 7 deletions
diff --git a/cpukit/dtc/libfdt/fdt.c b/cpukit/dtc/libfdt/fdt.c
index 8bbf2dc894..5f1e57a5bd 100644
--- a/cpukit/dtc/libfdt/fdt.c
+++ b/cpukit/dtc/libfdt/fdt.c
@@ -79,9 +79,64 @@ int fdt_ro_probe_(const void *fdt)
return 0;
}
+static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off)
+{
+ return (off >= hdrsize) && (off <= totalsize);
+}
+
+static int check_block_(uint32_t hdrsize, uint32_t totalsize,
+ uint32_t base, uint32_t size)
+{
+ if (!check_off_(hdrsize, totalsize, base))
+ return 0; /* block start out of bounds */
+ if ((base + size) < base)
+ return 0; /* overflow */
+ if (!check_off_(hdrsize, totalsize, base + size))
+ return 0; /* block end out of bounds */
+ return 1;
+}
+
int fdt_check_header(const void *fdt)
{
- return fdt_ro_probe_(fdt);
+ size_t hdrsize = FDT_V16_SIZE;
+
+ if (fdt_magic(fdt) != FDT_MAGIC)
+ return -FDT_ERR_BADMAGIC;
+ if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
+ || (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION))
+ return -FDT_ERR_BADVERSION;
+ if (fdt_version(fdt) < fdt_last_comp_version(fdt))
+ return -FDT_ERR_BADVERSION;
+
+ if (fdt_version(fdt) >= 17)
+ hdrsize = FDT_V17_SIZE;
+
+ if ((fdt_totalsize(fdt) < hdrsize)
+ || (fdt_totalsize(fdt) > INT_MAX))
+ return -FDT_ERR_TRUNCATED;
+
+ /* Bounds check memrsv block */
+ if (!check_off_(hdrsize, fdt_totalsize(fdt), fdt_off_mem_rsvmap(fdt)))
+ return -FDT_ERR_TRUNCATED;
+
+ /* Bounds check structure block */
+ if (fdt_version(fdt) < 17) {
+ if (!check_off_(hdrsize, fdt_totalsize(fdt),
+ fdt_off_dt_struct(fdt)))
+ return -FDT_ERR_TRUNCATED;
+ } else {
+ if (!check_block_(hdrsize, fdt_totalsize(fdt),
+ fdt_off_dt_struct(fdt),
+ fdt_size_dt_struct(fdt)))
+ return -FDT_ERR_TRUNCATED;
+ }
+
+ /* Bounds check strings block */
+ if (!check_block_(hdrsize, fdt_totalsize(fdt),
+ fdt_off_dt_strings(fdt), fdt_size_dt_strings(fdt)))
+ return -FDT_ERR_TRUNCATED;
+
+ return 0;
}
const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
diff --git a/cpukit/include/libfdt.h b/cpukit/include/libfdt.h
index c8c00fa865..837c5e27ee 100644
--- a/cpukit/include/libfdt.h
+++ b/cpukit/include/libfdt.h
@@ -90,8 +90,9 @@
/* Error codes: codes for bad device tree blobs */
#define FDT_ERR_TRUNCATED 8
- /* FDT_ERR_TRUNCATED: Structure block of the given device tree
- * ends without an FDT_END tag. */
+ /* FDT_ERR_TRUNCATED: FDT or a sub-block is improperly
+ * terminated (overflows, goes outside allowed bounds, or
+ * isn't properly terminated). */
#define FDT_ERR_BADMAGIC 9
/* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a
* device tree at all - it is missing the flattened device
@@ -244,18 +245,20 @@ fdt_set_hdr_(size_dt_struct);
#undef fdt_set_hdr_
/**
- * fdt_check_header - sanity check a device tree or possible device tree
+ * fdt_check_header - sanity check a device tree header
* @fdt: pointer to data which might be a flattened device tree
*
* fdt_check_header() checks that the given buffer contains what
- * appears to be a flattened device tree with sane information in its
- * header.
+ * appears to be a flattened device tree, and that the header contains
+ * valid information (to the extent that can be determined from the
+ * header alone).
*
* returns:
* 0, if the buffer appears to contain a valid device tree
* -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE, standard meanings, as above
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_TRUNCATED, standard meanings, as above
*/
int fdt_check_header(const void *fdt);
diff --git a/cpukit/include/libfdt_env.h b/cpukit/include/libfdt_env.h
index bd24746287..eb2053845c 100644
--- a/cpukit/include/libfdt_env.h
+++ b/cpukit/include/libfdt_env.h
@@ -56,6 +56,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <limits.h>
#ifdef __CHECKER__
#define FDT_FORCE __attribute__((force))