The FIT devicetree loader can return either data inside the FIT or a
newly allocated buffer. Callers which only need a temporary result
cannot tell whether it is safe to free.

Report when the returned buffer must be freed. This lets the new
fdt fit command copy the result to its chosen address without leaking
memory or freeing part of the original FIT. Also release temporary
allocations when they are no longer needed, including on error.

Reviewed-by: Simon Glass <[email protected]>
Signed-off-by: Carlo Caione <[email protected]>
---
 boot/image-fdt.c |  3 ++-
 boot/image-fit.c | 68 +++++++++++++++++++++++++++++++++++++++++++++-----------
 include/image.h  |  5 ++++-
 3 files changed, 61 insertions(+), 15 deletions(-)

diff --git a/boot/image-fdt.c b/boot/image-fdt.c
index 956a3d97c42..e65114ff835 100644
--- a/boot/image-fdt.c
+++ b/boot/image-fdt.c
@@ -468,7 +468,8 @@ static int select_fdt(struct bootm_headers *images, const 
char *select, u8 arch,
                                fdt_noffset = boot_get_fdt_fit(images, fdt_addr,
                                                               &fit_uname_fdt,
                                                               
&fit_uname_config,
-                                                              arch, &load, 
&len);
+                                                              arch, &load, 
&len,
+                                                              NULL);
 
                                if (fdt_noffset < 0)
                                        return -ENOENT;
diff --git a/boot/image-fit.c b/boot/image-fit.c
index 60e47db5fb0..4f4e7b4e06a 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -2242,10 +2242,13 @@ static inline int fit_image_load_storage(struct 
bootm_headers *images,
 }
 #endif
 
-int fit_image_load(struct bootm_headers *images, ulong addr,
-                  const char **fit_unamep, const char **fit_uname_configp,
-                  int arch, int ph_type, int bootstage_id,
-                  enum fit_load_op load_op, ulong *datap, ulong *lenp)
+/* Track load-time copies without changing the public fit_image_load() API. */
+static int fit_image_load_alloc(struct bootm_headers *images, ulong addr,
+                               const char **fit_unamep,
+                               const char **fit_uname_configp,
+                               int arch, int ph_type, int bootstage_id,
+                               enum fit_load_op load_op, ulong *datap,
+                               ulong *lenp, bool *ownedp)
 {
        int image_type = image_ph_type(ph_type);
        int cfg_noffset, noffset;
@@ -2255,6 +2258,7 @@ int fit_image_load(struct bootm_headers *images, ulong 
addr,
        const void *fit;
        void *buf;
        void *loadbuf;
+       bool load_owned = false;
        size_t size;
        int type_ok, os_ok;
        ulong load, load_end, data, len;
@@ -2262,6 +2266,8 @@ int fit_image_load(struct bootm_headers *images, ulong 
addr,
        const char *prop_name;
        int ret;
 
+       if (ownedp)
+               *ownedp = false;
        fit = map_sysmem(addr, 0);
        fit_uname = fit_unamep ? *fit_unamep : NULL;
        fit_uname_config = fit_uname_configp ? *fit_uname_configp : NULL;
@@ -2487,6 +2493,9 @@ int fit_image_load(struct bootm_headers *images, ulong 
addr,
                log_debug("decompressing image\n");
                if (load == data) {
                        loadbuf = aligned_alloc(8, max_decomp_len);
+                       if (!loadbuf)
+                               return -ENOMEM;
+                       load_owned = true;
                        load = map_to_sysmem(loadbuf);
                } else {
                        loadbuf = map_sysmem(load, max_decomp_len);
@@ -2494,13 +2503,17 @@ int fit_image_load(struct bootm_headers *images, ulong 
addr,
                if (image_decomp(comp, load, data, image_type,
                                loadbuf, buf, len, max_decomp_len, &load_end)) {
                        printf("Error decompressing %s\n", prop_name);
-
+                       if (load_owned)
+                               free(loadbuf);
                        return -ENOEXEC;
                }
                len = load_end - load;
        } else if (load_op != FIT_LOAD_IGNORED && image_type == IH_TYPE_FLATDT 
&&
                   (load & 7)) {
                loadbuf = aligned_alloc(8, len);
+               if (!loadbuf)
+                       return -ENOMEM;
+               load_owned = true;
                load = map_to_sysmem(loadbuf);
                memcpy(loadbuf, buf, len);
        } else if (load != data) {
@@ -2517,6 +2530,8 @@ int fit_image_load(struct bootm_headers *images, ulong 
addr,
        if (load_op != FIT_LOAD_IGNORED && image_type == IH_TYPE_FLATDT &&
            fdt_check_header(loadbuf)) {
                puts("Subimage data is not a FDT\n");
+               if (load_owned)
+                       free(loadbuf);
                return -ENOEXEC;
        }
 
@@ -2526,6 +2541,8 @@ int fit_image_load(struct bootm_headers *images, ulong 
addr,
 
        *datap = load;
        *lenp = len;
+       if (ownedp)
+               *ownedp = load_owned;
        if (fit_unamep)
                *fit_unamep = (char *)fit_uname;
        if (fit_uname_configp)
@@ -2535,6 +2552,16 @@ int fit_image_load(struct bootm_headers *images, ulong 
addr,
        return noffset;
 }
 
+int fit_image_load(struct bootm_headers *images, ulong addr,
+                  const char **fit_unamep, const char **fit_uname_configp,
+                  int arch, int ph_type, int bootstage_id,
+                  enum fit_load_op load_op, ulong *datap, ulong *lenp)
+{
+       return fit_image_load_alloc(images, addr, fit_unamep, fit_uname_configp,
+                                   arch, ph_type, bootstage_id, load_op, datap,
+                                   lenp, NULL);
+}
+
 int boot_get_setup_fit(struct bootm_headers *images, uint8_t arch,
                       ulong *setup_start, ulong *setup_len)
 {
@@ -2631,7 +2658,7 @@ out:
 
 int boot_get_fdt_fit(struct bootm_headers *images, ulong addr,
                     const char **fit_unamep, const char **fit_uname_configp,
-                    int arch, ulong *datap, ulong *lenp)
+                    int arch, ulong *datap, ulong *lenp, bool *ownedp)
 {
        int fdt_noffset, cfg_noffset, count;
        const void *fit;
@@ -2639,7 +2666,8 @@ int boot_get_fdt_fit(struct bootm_headers *images, ulong 
addr,
        const char *fit_uname_config = NULL;
        char *fit_uname_config_copy = NULL;
        char *next_config = NULL;
-       ulong load, len;
+       ulong load = 0, len = 0;
+       bool load_owned = false;
 #ifdef CONFIG_OF_LIBFDT_OVERLAY
        ulong ovload, ovlen, ovcopylen, need;
        const char *uconfig;
@@ -2651,6 +2679,8 @@ int boot_get_fdt_fit(struct bootm_headers *images, ulong 
addr,
 #endif
 
        fit_uname = fit_unamep ? *fit_unamep : NULL;
+       if (ownedp)
+               *ownedp = false;
 
        if (fit_uname_configp && *fit_uname_configp) {
                fit_uname_config_copy = strdup(*fit_uname_configp);
@@ -2664,11 +2694,11 @@ int boot_get_fdt_fit(struct bootm_headers *images, 
ulong addr,
                        fit_uname_config = fit_uname_config_copy;
        }
 
-       fdt_noffset = fit_image_load(images,
-               addr, &fit_uname, &fit_uname_config,
-               arch, IH_TYPE_FLATDT,
-               BOOTSTAGE_ID_FIT_FDT_START,
-               FIT_LOAD_OPTIONAL, &load, &len);
+       fdt_noffset = fit_image_load_alloc(images, addr, &fit_uname,
+                                          &fit_uname_config, arch, 
IH_TYPE_FLATDT,
+                                          BOOTSTAGE_ID_FIT_FDT_START,
+                                          FIT_LOAD_OPTIONAL, &load, &len,
+                                          &load_owned);
 
        if (fdt_noffset < 0)
                goto out;
@@ -2699,6 +2729,10 @@ int boot_get_fdt_fit(struct bootm_headers *images, ulong 
addr,
        err = boot_get_fdt_fit_into_buffer(map_sysmem(load, len), len,
                                           CONFIG_SYS_FDT_PAD, 0, &base_buf,
                                           &base_buf_size);
+       if (load_owned) {
+               free(map_sysmem(load, len));
+               load_owned = false;
+       }
        if (err < 0) {
                if (err != -ENOMEM)
                        printf("Required FDT copy for applying DTOs failed: 
%s\n",
@@ -2809,9 +2843,17 @@ int boot_get_fdt_fit(struct bootm_headers *images, ulong 
addr,
 
 out:
 #ifdef CONFIG_OF_LIBFDT_OVERLAY
-       if (fdt_noffset >= 0 && base_buf)
+       if (fdt_noffset >= 0 && base_buf) {
                load = map_to_sysmem(base_buf);
+               load_owned = true;
+       }
 #endif
+       if (fdt_noffset < 0 && load_owned) {
+               free(map_sysmem(load, len));
+               load_owned = false;
+       }
+       if (ownedp)
+               *ownedp = load_owned;
        if (datap)
                *datap = load;
        if (lenp)
diff --git a/include/image.h b/include/image.h
index d991b4c2bc3..4ef5344c718 100644
--- a/include/image.h
+++ b/include/image.h
@@ -721,12 +721,15 @@ int boot_get_setup_fit(struct bootm_headers *images, 
uint8_t arch,
  * @param arch         Expected architecture (IH_ARCH_...)
  * @param datap                Returns address of loaded image
  * @param lenp         Returns length of loaded image
+ * @param ownedp       Returns true if the loaded image is separately allocated
+ *                     and must be freed by the caller; may be NULL if the 
caller
+ *                     does not need this information
  *
  * Return: node offset of base image, or -ve error code on error
  */
 int boot_get_fdt_fit(struct bootm_headers *images, ulong addr,
                     const char **fit_unamep, const char **fit_uname_configp,
-                    int arch, ulong *datap, ulong *lenp);
+                    int arch, ulong *datap, ulong *lenp, bool *ownedp);
 
 /**
  * fit_image_load() - load an image from a FIT

-- 
2.56.0

Reply via email to