summaryrefslogtreecommitdiff
path: root/common/spl/spl_fit.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/spl/spl_fit.c')
-rw-r--r--common/spl/spl_fit.c98
1 files changed, 63 insertions, 35 deletions
diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c
index 46ebcabe56a..18bff7b8d4a 100644
--- a/common/spl/spl_fit.c
+++ b/common/spl/spl_fit.c
@@ -204,6 +204,9 @@ static int get_aligned_image_size(struct spl_load_info *info, int data_size,
* If the FIT node does not contain a "load" (address) property,
* the image gets loaded to the address pointed to by the
* load_addr member in this struct, if load_addr is not 0
+ * @max_size: maximum number of bytes that may be written to the
+ * destination; an image whose data exceeds this is rejected
+ * before it is read from the device
*
* Return: 0 on success, -EBADSLT if this image is not the correct phase
* (for CONFIG_BOOTMETH_VBE_SIMPLE_FW), or another negative error number on
@@ -211,7 +214,7 @@ static int get_aligned_image_size(struct spl_load_info *info, int data_size,
*/
static int load_simple_fit(struct spl_load_info *info, ulong fit_offset,
const struct spl_fit_info *ctx, int node,
- struct spl_image_info *image_info)
+ struct spl_image_info *image_info, ulong max_size)
{
int offset;
size_t length;
@@ -291,6 +294,23 @@ static int load_simple_fit(struct spl_load_info *info, ulong fit_offset,
return 0;
}
+ /*
+ * data-size is excluded from the configuration signature (it
+ * is in exc_prop[] in image-fit-sig.c), so it stays attacker
+ * controlled even after fit_config_verify() succeeds. The
+ * image hash is only verified after the device read below, so
+ * an oversized value has to be rejected here.
+ *
+ * Bail out before get_aligned_image_size() runs on a hostile
+ * len: that helper does its arithmetic in int and would
+ * invoke signed-integer overflow on a value close to or above
+ * INT_MAX. The block-aligned check further down is the
+ * mathematically binding one, since size is len rounded up to
+ * the device block length.
+ */
+ if ((ulong)len > max_size)
+ goto too_big;
+
if (spl_decompression_enabled() &&
(image_comp == IH_COMP_GZIP || image_comp == IH_COMP_LZMA))
src_ptr = map_sysmem(ALIGN(CONFIG_SYS_LOAD_ADDR, ARCH_DMA_MINALIGN), len);
@@ -302,6 +322,15 @@ static int load_simple_fit(struct spl_load_info *info, ulong fit_offset,
size = get_aligned_image_size(info, length, offset);
read_offset = fit_offset + get_aligned_image_offset(info,
offset);
+
+ /*
+ * info->read() transfers the block-aligned size into the
+ * destination, so this is the bound that actually matters;
+ * len was rejected above only to keep this computation safe.
+ */
+ if (size > max_size)
+ goto too_big;
+
log_debug("reading from offset %x / %lx size %lx to %p: ",
offset, read_offset, size, src_ptr);
@@ -372,6 +401,11 @@ static int load_simple_fit(struct spl_load_info *info, ulong fit_offset,
upl_add_image(fit, node, load_addr, length);
return 0;
+
+too_big:
+ printf("%s: FIT image too large (data-size %u, max %lu)\n",
+ __func__, (u32)len, max_size);
+ return -EFBIG;
}
static bool os_takes_devicetree(uint8_t os)
@@ -427,7 +461,8 @@ static int spl_fit_append_fdt(struct spl_image_info *spl_image,
spl_image->fdt_addr = map_sysmem(image_info.load_addr, size);
memcpy(spl_image->fdt_addr, gd->fdt_blob, size);
} else {
- ret = load_simple_fit(info, offset, ctx, node, &image_info);
+ ret = load_simple_fit(info, offset, ctx, node, &image_info,
+ CONFIG_SYS_BOOTM_LEN);
if (ret < 0)
return ret;
@@ -479,7 +514,8 @@ static int spl_fit_append_fdt(struct spl_image_info *spl_image,
}
image_info.load_addr = (ulong)tmpbuffer;
ret = load_simple_fit(info, offset, ctx, node,
- &image_info);
+ &image_info,
+ CONFIG_SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ);
if (ret == -EBADSLT)
continue;
else if (ret < 0)
@@ -687,7 +723,8 @@ static int spl_fit_load_fpga(struct spl_fit_info *ctx,
warn_deprecated("'fpga' property in config node. Use 'loadables'");
/* Load the image and set up the fpga_image structure */
- ret = load_simple_fit(info, offset, ctx, node, &fpga_image);
+ ret = load_simple_fit(info, offset, ctx, node, &fpga_image,
+ CONFIG_SYS_BOOTM_LEN);
if (ret) {
printf("%s: Cannot load the FPGA: %i\n", __func__, ret);
return ret;
@@ -775,7 +812,7 @@ static int spl_simple_fit_parse(struct spl_fit_info *ctx)
if (ctx->conf_node < 0)
return -EINVAL;
- if (IS_ENABLED(CONFIG_SPL_FIT_SIGNATURE)) {
+ if (CONFIG_IS_ENABLED(FIT_SIGNATURE)) {
printf("## Checking hash(es) for config %s ... ",
fit_get_name(ctx->fit, ctx->conf_node, NULL));
if (fit_config_verify(ctx->fit, ctx->conf_node))
@@ -849,7 +886,8 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
}
/* Load the image and set up the spl_image structure */
- ret = load_simple_fit(info, offset, &ctx, node, spl_image);
+ ret = load_simple_fit(info, offset, &ctx, node, spl_image,
+ CONFIG_SYS_BOOTM_LEN);
if (ret)
return ret;
@@ -890,7 +928,8 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
continue;
image_info.load_addr = 0;
- ret = load_simple_fit(info, offset, &ctx, node, &image_info);
+ ret = load_simple_fit(info, offset, &ctx, node, &image_info,
+ CONFIG_SYS_BOOTM_LEN);
if (ret < 0 && ret != -EBADSLT) {
printf("%s: can't load image loadables index %d (ret = %d)\n",
__func__, index, ret);
@@ -955,22 +994,12 @@ int spl_load_fit_image(struct spl_image_info *spl_image,
int idx, conf_noffset;
int ret;
-#ifdef CONFIG_SPL_FIT_SIGNATURE
- images.verify = 1;
-#endif
+ images.verify = CONFIG_IS_ENABLED(FIT_SIGNATURE);
+
ret = fit_image_load(&images, virt_to_phys((void *)header),
- NULL, &fit_uname_config,
- IH_ARCH_DEFAULT, IH_TYPE_STANDALONE, -1,
- FIT_LOAD_OPTIONAL, &fw_data, &fw_len);
- if (ret >= 0) {
- printf("DEPRECATED: 'standalone = ' property.");
- printf("Please use either 'firmware =' or 'kernel ='\n");
- } else {
- ret = fit_image_load(&images, virt_to_phys((void *)header),
- NULL, &fit_uname_config, IH_ARCH_DEFAULT,
- IH_TYPE_FIRMWARE, -1, FIT_LOAD_OPTIONAL,
- &fw_data, &fw_len);
- }
+ NULL, &fit_uname_config, IH_ARCH_DEFAULT,
+ IH_TYPE_FIRMWARE, -1, FIT_LOAD_OPTIONAL,
+ &fw_data, &fw_len);
if (ret < 0) {
ret = fit_image_load(&images, virt_to_phys((void *)header),
@@ -993,21 +1022,21 @@ int spl_load_fit_image(struct spl_image_info *spl_image,
debug(PHASE_PROMPT "payload image: %32s load addr: 0x%lx size: %d\n",
spl_image->name, spl_image->load_addr, spl_image->size);
-#ifdef CONFIG_SPL_FIT_SIGNATURE
- images.verify = 1;
-#endif
+ images.verify = CONFIG_IS_ENABLED(FIT_SIGNATURE);
+
ret = fit_image_load(&images, virt_to_phys((void *)header), NULL,
&fit_uname_config, IH_ARCH_DEFAULT, IH_TYPE_FLATDT,
-1, FIT_LOAD_OPTIONAL, &dt_data, &dt_len);
if (ret >= 0) {
- spl_image->fdt_addr = (void *)dt_data;
-
if (spl_image->os == IH_OS_U_BOOT) {
/* HACK: U-Boot expects FDT at a specific address */
- fdt_hack = spl_image->load_addr + spl_image->size;
- fdt_hack = (fdt_hack + 3) & ~3;
- debug("Relocating FDT to %p\n", spl_image->fdt_addr);
- memcpy((void *)fdt_hack, spl_image->fdt_addr, dt_len);
+ fdt_hack = ALIGN(spl_image->load_addr + spl_image->size, 8);
+ debug("Relocating FDT to %p\n", (void *)fdt_hack);
+ memcpy(map_sysmem(fdt_hack, dt_len),
+ map_sysmem(dt_data, 0), dt_len);
+ spl_image->fdt_addr = (void *)fdt_hack;
+ } else {
+ spl_image->fdt_addr = (void *)dt_data;
}
}
@@ -1021,10 +1050,9 @@ int spl_load_fit_image(struct spl_image_info *spl_image,
FIT_LOADABLE_PROP, idx,
NULL), uname;
idx++) {
-#ifdef CONFIG_SPL_FIT_SIGNATURE
- images.verify = 1;
-#endif
- ret = fit_image_load(&images, (ulong)header,
+ images.verify = CONFIG_IS_ENABLED(FIT_SIGNATURE);
+
+ ret = fit_image_load(&images, virt_to_phys((void *)header),
&uname, &fit_uname_config,
IH_ARCH_DEFAULT, IH_TYPE_LOADABLE, -1,
FIT_LOAD_OPTIONAL_NON_ZERO,