From: Thierry Reding <tred...@nvidia.com>

The current implementation of fdtdec_get_addr_size() assumes that the
sizes of fdt_addr_t and fdt_size_t match the number of cells specified
by the #address-cells and #size-cells properties. However, there is no
reason why that needs to be the case, so the function potentially
decodes address and size wrongly.

Furthermore the function casts an array of FDT cells (32-bit unsigned)
to fdt_addr_t/fdt_size_t and dereferences them directly. This can cause
aborts on 64-bit architectures where fdt_addr_t/fdt_size_t are 64 bits
wide, because cells are only guaranteed to be aligned to 32 bits. Fix
this by reading in the address and size one cell at a time.

Cc: Hanna Hawa <han...@marvell.com>
Cc: Simon Glass <s...@chromium.org>
Signed-off-by: Thierry Reding <tred...@nvidia.com>
---
 lib/fdtdec.c | 56 ++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 36 insertions(+), 20 deletions(-)

diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index 613cc8494a74..fd244440381e 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -89,29 +89,45 @@ const char *fdtdec_get_compatible(enum fdt_compat_id id)
 fdt_addr_t fdtdec_get_addr_size(const void *blob, int node,
                const char *prop_name, fdt_size_t *sizep)
 {
-       const fdt_addr_t *cell;
-       int len;
+       const fdt32_t *ptr, *end;
+       int parent, na, ns, len;
+       fdt_addr_t addr;
 
        debug("%s: %s: ", __func__, prop_name);
-       cell = fdt_getprop(blob, node, prop_name, &len);
-       if (cell && ((!sizep && len == sizeof(fdt_addr_t)) ||
-                    len == sizeof(fdt_addr_t) * 2)) {
-               fdt_addr_t addr = fdt_addr_to_cpu(*cell);
-               if (sizep) {
-                       const fdt_size_t *size;
-
-                       size = (fdt_size_t *)((char *)cell +
-                                       sizeof(fdt_addr_t));
-                       *sizep = fdt_size_to_cpu(*size);
-                       debug("addr=%08lx, size=%08lx\n",
-                             (ulong)addr, (ulong)*sizep);
-               } else {
-                       debug("%08lx\n", (ulong)addr);
-               }
-               return addr;
+
+       parent = fdt_parent_offset(blob, node);
+       if (parent < 0) {
+               debug("(no parent found)\n");
+               return FDT_ADDR_T_NONE;
        }
-       debug("(not found)\n");
-       return FDT_ADDR_T_NONE;
+
+       na = fdt_address_cells(blob, parent);
+       ns = fdt_size_cells(blob, parent);
+
+       ptr = fdt_getprop(blob, node, prop_name, &len);
+       if (!ptr) {
+               debug("(not found)\n");
+               return FDT_ADDR_T_NONE;
+       }
+
+       end = ptr + len / sizeof(*ptr);
+
+       if (ptr + na + ns > end) {
+               debug("(not enough data: expected %d bytes, got %d bytes)\n",
+                     (na + ns) * 4, len);
+               return FDT_ADDR_T_NONE;
+       }
+
+       addr = fdtdec_get_number(ptr, na);
+
+       if (sizep) {
+               *sizep = fdtdec_get_number(ptr + na, ns);
+               debug("addr=%pa, size=%pa\n", &addr, sizep);
+       } else {
+               debug("%pa\n", &addr);
+       }
+
+       return addr;
 }
 
 fdt_addr_t fdtdec_get_addr(const void *blob, int node,
-- 
2.3.2

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to