[PATCH] spl: fit: Prefer a malloc()'d buffer for loading images
Simon Glass
sjg at chromium.org
Tue Nov 3 16:11:52 CET 2020
+Tom Rini too
On Wed, 21 Oct 2020 at 17:33, Alexandru Gagniuc <mr.nuke.me at gmail.com> wrote:
>
> Fit images were loaded to a buffer provided by spl_get_load_buffer().
> This may work when the FIT image is small and fits between the start
> of DRAM and SYS_TEXT_BASE.
>
> One problem with this approach is that the location of the buffer may
> be manipulated by changing the 'size' field of the FIT. A maliciously
> crafted FIT image could place the buffer over executable code and be
> able to take control of SPL. This is unacceptable for secure boot of
> signed FIT images.
>
> Another problem is with larger FIT images, usually containing one or
> more linux kernels. In such cases the buffer be be large enough so as
> to start before DRAM (Figure I). Trying to load an image in this case
> has undefined behavior.
> For example, on stm32mp1, the MMC controller hits a RX overrun error,
> and aborts loading.
> _________________
> | FIT Image |
> | |
> /===================\ /=====================\
> || DRAM || | DRAM |
> || || | |
> ||_________________|| SYS_TEXT_BASE | ___________________ |
> | | || FIT Image ||
> | | || ||
> | _________________ | SYS_SPL_MALLOC_START || _________________ ||
> || malloc() data || ||| malloc() data |||
> ||_________________|| |||_________________|||
> | | ||___________________||
> | | | |
>
> Figure I Figure II
>
> One possibility that was analyzed was to remove the negative offset,
> such that the buffer starts at SYS_TEXT_BASE. This is not a proper
> solution because on a number of platforms, the malloc buffer() is
> placed at a fixed address, usually after SYS_TEXT_BASE. A large
> enough FIT image could cause the malloc()'d data to be overwritten
> (Figure II) when loading.
>
> /======================\
> | DRAM |
> | |
> | | CONFIG_SYS_TEXT_BASE
> | |
> | |
> | ____________________ | CONFIG_SYS_SPL_MALLOC_START
> || malloc() data ||
> || ||
> || __________________ ||
> ||| FIT Image |||
> ||| |||
> ||| |||
>
> Figure III
>
> The solution proposed here is to replace the ad-hoc heuristics of
> spl_get_load_buffer() with malloc(). This provides two advantages:
> * Bounds checking of the buffer region
> * Guarantees the buffer does not conflict with other memory
>
> The first problem is solved by constraining the buffer such that it
> will not overlap currently executing code. This eliminates the chance
> of a malicious FIT being able to replace the executing SPL code prior
> to signature checking.
>
> The second problem is solved in conjunction with increasing
> CONFIG_SYS_SPL_MALLOC_SIZE. Since the SPL malloc() region is
> carefully crafted on a per-platform basis, the chances of memory
> conflicts are virtually eliminated.
>
> Signed-off-by: Alexandru Gagniuc <mr.nuke.me at gmail.com>
> ---
>
> Contingency plan:
>
> If the malloc() fails, the backup plan is to use spl_get_load_buffer()
> with the negative offset eliminated. This puts the buffer at
> CONFIG_SYS_TEXT_BASE as a final measure. We can't use the negative
> offset because that would re-introduce the first prooblem.
>
> Expected fallout:
>
> This only affects the FIT image loading path, so no breakage is
> expected for default configurations. Most configs have a sufficiently
> large SYS_SPL_MALLOC_SIZE, with the most common value at 1 MB. This
> adequate for the majority of u-boot FIT images.
>
> No significant breakage is expected.
>
>
> common/spl/spl_fit.c | 37 ++++++++++++++++++++++---------------
> 1 file changed, 22 insertions(+), 15 deletions(-)
>
> diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c
> index a90d821c82..98a7531f72 100644
> --- a/common/spl/spl_fit.c
> +++ b/common/spl/spl_fit.c
> @@ -472,6 +472,23 @@ static int spl_fit_image_get_os(const void *fit, int noffset, uint8_t *os)
> #endif
> }
>
> +/*
> + * The purpose of the FIT load buffer is to provide a memory location that is
> + * independent of the load address of any FIT component.
> + */
> +static void *spl_get_fit_load_buffer(size_t size)
> +{
> + void *buf;
> +
> + buf = malloc(size);
> + if (!buf) {
> + pr_err("Could not get FIT buffer of %lu bytes\n", (ulong)size);
> + pr_err("\tcheck CONFIG_SYS_SPL_MALLOC_SIZE\n");
> + buf = spl_get_load_buffer(0, size);
> + }
> + return buf;
> +}
> +
> /*
> * Weak default function to allow customizing SPL fit loading for load-only
> * use cases by allowing to skip the parsing/processing of the FIT contents
> @@ -486,12 +503,12 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
> struct spl_load_info *info, ulong sector, void *fit)
> {
> int sectors;
> - ulong size;
> + ulong size, hsize;
> unsigned long count;
> struct spl_image_info image_info;
> int node = -1;
> int images, ret;
> - int base_offset, hsize, align_len = ARCH_DMA_MINALIGN - 1;
> + int base_offset;
> int index = 0;
> int firmware_node;
>
> @@ -507,24 +524,14 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
>
> /*
> * So far we only have one block of data from the FIT. Read the entire
> - * thing, including that first block, placing it so it finishes before
> - * where we will load the image.
> - *
> - * Note that we will load the image such that its first byte will be
> - * at the load address. Since that byte may be part-way through a
> - * block, we may load the image up to one block before the load
> - * address. So take account of that here by subtracting an addition
> - * block length from the FIT start position.
> - *
> - * In fact the FIT has its own load address, but we assume it cannot
> - * be before CONFIG_SYS_TEXT_BASE.
> + * thing, including that first block.
> *
> * For FIT with data embedded, data is loaded as part of FIT image.
> * For FIT with external data, data is not loaded in this step.
> */
> - hsize = (size + info->bl_len + align_len) & ~align_len;
> - fit = spl_get_load_buffer(-hsize, hsize);
> sectors = get_aligned_image_size(info, size, 0);
> + hsize = sectors * info->bl_len;
> + fit = spl_get_fit_load_buffer(hsize);
> count = info->read(info, sector, sectors, fit);
> debug("fit read sector %lx, sectors=%d, dst=%p, count=%lu, size=0x%lx\n",
> sector, sectors, fit, count, size);
> --
> 2.26.2
>
More information about the U-Boot
mailing list