summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/avb_verify.c29
-rw-r--r--common/bloblist.c8
-rw-r--r--common/board_f.c2
-rw-r--r--common/cyclic.c37
-rw-r--r--common/hash.c21
-rw-r--r--common/spl/spl_atf.c18
-rw-r--r--common/spl/spl_fit.c51
7 files changed, 131 insertions, 35 deletions
diff --git a/common/avb_verify.c b/common/avb_verify.c
index 29a3272579c..76c523fd0ba 100644
--- a/common/avb_verify.c
+++ b/common/avb_verify.c
@@ -452,6 +452,7 @@ static AvbIOResult mmc_byte_io(AvbOps *ops,
u64 start_offset, start_sector, sectors, residue;
u8 *tmp_buf;
size_t io_cnt = 0;
+ AvbIOResult io_ret = AVB_IO_RESULT_OK;
if (!partition || !buffer || io_type > IO_WRITE)
return AVB_IO_RESULT_ERROR_IO;
@@ -460,8 +461,10 @@ static AvbIOResult mmc_byte_io(AvbOps *ops,
if (!part)
return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
- if (!part->info.blksz)
- return AVB_IO_RESULT_ERROR_IO;
+ if (!part->info.blksz) {
+ io_ret = AVB_IO_RESULT_ERROR_IO;
+ goto out;
+ }
start_offset = calc_offset(part, offset);
while (num_bytes) {
@@ -489,7 +492,8 @@ static AvbIOResult mmc_byte_io(AvbOps *ops,
if (ret != 1) {
printf("%s: read error (%ld, %lld)\n",
__func__, ret, start_sector);
- return AVB_IO_RESULT_ERROR_IO;
+ io_ret = AVB_IO_RESULT_ERROR_IO;
+ goto out;
}
/*
* if this is not aligned at sector start,
@@ -506,7 +510,8 @@ static AvbIOResult mmc_byte_io(AvbOps *ops,
if (ret != 1) {
printf("%s: read error (%ld, %lld)\n",
__func__, ret, start_sector);
- return AVB_IO_RESULT_ERROR_IO;
+ io_ret = AVB_IO_RESULT_ERROR_IO;
+ goto out;
}
memcpy((void *)tmp_buf +
start_offset % part->info.blksz,
@@ -517,7 +522,8 @@ static AvbIOResult mmc_byte_io(AvbOps *ops,
if (ret != 1) {
printf("%s: write error (%ld, %lld)\n",
__func__, ret, start_sector);
- return AVB_IO_RESULT_ERROR_IO;
+ io_ret = AVB_IO_RESULT_ERROR_IO;
+ goto out;
}
}
@@ -543,7 +549,8 @@ static AvbIOResult mmc_byte_io(AvbOps *ops,
if (!ret) {
printf("%s: sector read error\n", __func__);
- return AVB_IO_RESULT_ERROR_IO;
+ io_ret = AVB_IO_RESULT_ERROR_IO;
+ goto out;
}
io_cnt += ret * part->info.blksz;
@@ -557,7 +564,9 @@ static AvbIOResult mmc_byte_io(AvbOps *ops,
if (io_type == IO_READ && out_num_read)
*out_num_read = io_cnt;
- return AVB_IO_RESULT_OK;
+out:
+ free(part);
+ return io_ret;
}
/**
@@ -867,12 +876,15 @@ static AvbIOResult get_unique_guid_for_partition(AvbOps *ops,
return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
uuid_size = sizeof(part->info.uuid);
- if (uuid_size > guid_buf_size)
+ if (uuid_size > guid_buf_size) {
+ free(part);
return AVB_IO_RESULT_ERROR_IO;
+ }
memcpy(guid_buf, part->info.uuid, uuid_size);
guid_buf[uuid_size - 1] = 0;
+ free(part);
return AVB_IO_RESULT_OK;
}
@@ -903,6 +915,7 @@ static AvbIOResult get_size_of_partition(AvbOps *ops,
return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
*out_size_num_bytes = part->info.blksz * part->info.size;
+ free(part);
return AVB_IO_RESULT_OK;
}
diff --git a/common/bloblist.c b/common/bloblist.c
index 51ae9cc50a5..846c8047f74 100644
--- a/common/bloblist.c
+++ b/common/bloblist.c
@@ -448,7 +448,7 @@ int bloblist_new(ulong addr, uint size, uint flags, uint align_log2)
hdr->align_log2 = align_log2 ? align_log2 : BLOBLIST_BLOB_ALIGN_LOG2;
hdr->chksum = 0;
gd->bloblist = hdr;
- gd->flags |= GD_FLG_BLOBLIST_READY;
+ gd->flags |= GD_FLG_BLOBLIST_HANDOFF;
return 0;
}
@@ -476,7 +476,7 @@ int bloblist_check(ulong addr, uint size)
return log_msg_ret("Bad checksum", -EIO);
}
gd->bloblist = hdr;
- gd->flags |= GD_FLG_BLOBLIST_READY;
+ gd->flags |= GD_FLG_BLOBLIST_HANDOFF;
return 0;
}
@@ -627,7 +627,7 @@ int bloblist_init(void)
int ret;
ulong addr = 0, size = CONFIG_BLOBLIST_SIZE;
- if (gd->flags & GD_FLG_BLOBLIST_READY) {
+ if (gd->flags & GD_FLG_BLOBLIST_HANDOFF) {
log_debug("Found existing bloblist size %x at %p\n",
gd->bloblist->total_size, gd->bloblist);
return 0;
@@ -689,7 +689,7 @@ int bloblist_check_reg_conv(ulong rfdt, ulong rzero, ulong rsig, ulong xlist)
if (rfdt != (ulong)bloblist_find(BLOBLISTT_CONTROL_FDT, 0)) {
/* Remove this bloblist from gd */
gd->bloblist = NULL;
- gd->flags &= ~GD_FLG_BLOBLIST_READY;
+ gd->flags &= ~GD_FLG_BLOBLIST_HANDOFF;
return -EIO;
}
diff --git a/common/board_f.c b/common/board_f.c
index 85b888d4bb8..9efcd9499a9 100644
--- a/common/board_f.c
+++ b/common/board_f.c
@@ -921,13 +921,13 @@ static void initcall_run_f(void)
* For simplicity it should remain an ordered list of function calls.
*/
INITCALL(setup_mon_len);
+ INITCALL(initf_malloc);
#if CONFIG_IS_ENABLED(OF_CONTROL)
INITCALL(fdtdec_setup);
#endif
#if CONFIG_IS_ENABLED(TRACE_EARLY)
INITCALL(trace_early_init);
#endif
- INITCALL(initf_malloc);
INITCALL(initf_upl);
INITCALL(log_init);
INITCALL(initf_bootstage); /* uses its own timer, so does not need DM */
diff --git a/common/cyclic.c b/common/cyclic.c
index ec952a01ee1..1cf5b25d1d8 100644
--- a/common/cyclic.c
+++ b/common/cyclic.c
@@ -22,17 +22,11 @@ DECLARE_GLOBAL_DATA_PTR;
void hw_watchdog_reset(void);
-struct hlist_head *cyclic_get_list(void)
-{
- /* Silence "discards 'volatile' qualifier" warning. */
- return (struct hlist_head *)&gd->cyclic_list;
-}
-
static bool cyclic_is_registered(const struct cyclic_info *cyclic)
{
const struct cyclic_info *c;
- hlist_for_each_entry(c, cyclic_get_list(), list) {
+ hlist_for_each_entry(c, &gd->cyclic_list, list) {
if (c == cyclic)
return true;
}
@@ -41,7 +35,7 @@ static bool cyclic_is_registered(const struct cyclic_info *cyclic)
}
void cyclic_register(struct cyclic_info *cyclic, cyclic_func_t func,
- uint64_t delay_us, const char *name)
+ u64 delay_us, const char *name)
{
cyclic_unregister(cyclic);
@@ -52,7 +46,7 @@ void cyclic_register(struct cyclic_info *cyclic, cyclic_func_t func,
cyclic->name = name;
cyclic->delay_us = delay_us;
cyclic->start_time_us = get_timer_us(0);
- hlist_add_head(&cyclic->list, cyclic_get_list());
+ hlist_add_head(&cyclic->list, &gd->cyclic_list);
}
void cyclic_unregister(struct cyclic_info *cyclic)
@@ -67,26 +61,41 @@ static void cyclic_run(void)
{
struct cyclic_info *cyclic;
struct hlist_node *tmp;
- uint64_t now, cpu_time;
+ u64 now, after, cpu_time;
+
+ /*
+ * Nothing to do if the list is empty. Also, schedule() can be
+ * called before timer infrastructure is ready, in which case
+ * calling get_timer_us() before the (empty) loop could cause
+ * a divide-by-0 or otherwise crash the system. No clients
+ * should be registered before the timer infrastructure is up,
+ * so the check for the list being empty should be
+ * ok. Otherwise, we would need a new GD_FLG_TIMERS_READY
+ * flag.
+ */
+ if (hlist_empty(&gd->cyclic_list))
+ return;
/* Prevent recursion */
if (gd->flags & GD_FLG_CYCLIC_RUNNING)
return;
gd->flags |= GD_FLG_CYCLIC_RUNNING;
- hlist_for_each_entry_safe(cyclic, tmp, cyclic_get_list(), list) {
+ now = get_timer_us(0);
+ hlist_for_each_entry_safe(cyclic, tmp, &gd->cyclic_list, list) {
/*
* Check if this cyclic function needs to get called, e.g.
* do not call the cyclic func too often
*/
- now = get_timer_us(0);
if (time_after_eq64(now, cyclic->next_call)) {
/* Call cyclic function and account it's cpu-time */
cyclic->next_call = now + cyclic->delay_us;
cyclic->func(cyclic);
+ after = get_timer_us(0);
cyclic->run_cnt++;
- cpu_time = get_timer_us(0) - now;
+ cpu_time = after - now;
cyclic->cpu_time_us += cpu_time;
+ now = after;
/* Check if cpu-time exceeds max allowed time */
if ((cpu_time > CONFIG_CYCLIC_MAX_CPU_TIME_US) &&
@@ -127,7 +136,7 @@ int cyclic_unregister_all(void)
struct cyclic_info *cyclic;
struct hlist_node *tmp;
- hlist_for_each_entry_safe(cyclic, tmp, cyclic_get_list(), list)
+ hlist_for_each_entry_safe(cyclic, tmp, &gd->cyclic_list, list)
cyclic_unregister(cyclic);
return 0;
diff --git a/common/hash.c b/common/hash.c
index 71c4bef5826..5cbb4926c1d 100644
--- a/common/hash.c
+++ b/common/hash.c
@@ -11,6 +11,7 @@
#ifndef USE_HOSTCC
#include <command.h>
+#include <dm.h>
#include <env.h>
#include <log.h>
#include <malloc.h>
@@ -20,6 +21,7 @@
#include <asm/global_data.h>
#include <asm/io.h>
#include <linux/errno.h>
+#include <u-boot/hash.h>
#else
#include "mkimage.h"
#include <linux/compiler_attributes.h>
@@ -614,7 +616,26 @@ int hash_command(const char *algo_name, int flags, struct cmd_tbl *cmdtp,
return CMD_RET_FAILURE;
buf = map_sysmem(addr, len);
+ if (CONFIG_IS_ENABLED(DM_HASH)) {
+ enum HASH_ALGO hash_algo;
+ int ret;
+
+ hash_algo = hash_algo_lookup_by_name(algo_name);
+ if (hash_algo != HASH_ALGO_INVALID) {
+ ret = hash_digest_wd_lookup(hash_algo, buf, len,
+ output,
+ algo->chunk_size);
+ if (ret && ret != -ENODEV && ret != -EOPNOTSUPP) {
+ unmap_sysmem(buf);
+ free(output);
+ return CMD_RET_FAILURE;
+ }
+ if (!ret)
+ goto done;
+ }
+ }
algo->hash_func_ws(buf, len, output, algo->chunk_size);
+done:
unmap_sysmem(buf);
/* Try to avoid code bloat when verify is not needed */
diff --git a/common/spl/spl_atf.c b/common/spl/spl_atf.c
index 8bc5db77395..17acd3665df 100644
--- a/common/spl/spl_atf.c
+++ b/common/spl/spl_atf.c
@@ -212,7 +212,9 @@ static void __noreturn bl31_entry(ulong bl31_entry, ulong bl32_entry,
static int spl_fit_images_find(void *blob, int os)
{
int parent, node, ndepth = 0;
+ int found = -FDT_ERR_NOTFOUND;
const void *data;
+ ulong val;
if (!blob)
return -FDT_ERR_BADMAGIC;
@@ -231,11 +233,23 @@ static int spl_fit_images_find(void *blob, int os)
if (!data)
continue;
- if (genimg_get_os_id(data) == os)
+ if (genimg_get_os_id(data) != os)
+ continue;
+
+ /*
+ * A multi-segment image, e.g. an OP-TEE ELF split by
+ * binman, is recorded as one node per segment, all with
+ * the same os. Only the segment holding the ELF entry
+ * point carries an entry property, so prefer that node.
+ */
+ if (!fit_image_get_entry(blob, node, &val))
return node;
+
+ if (found < 0)
+ found = node;
};
- return -FDT_ERR_NOTFOUND;
+ return found;
}
ulong spl_fit_images_get_entry(void *blob, int node)
diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c
index d89384449b3..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;
@@ -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);