diff options
Diffstat (limited to 'common')
| -rw-r--r-- | common/avb_verify.c | 29 | ||||
| -rw-r--r-- | common/bloblist.c | 8 | ||||
| -rw-r--r-- | common/board_f.c | 2 | ||||
| -rw-r--r-- | common/cyclic.c | 37 | ||||
| -rw-r--r-- | common/hash.c | 21 | ||||
| -rw-r--r-- | common/spl/spl_atf.c | 18 | ||||
| -rw-r--r-- | common/spl/spl_fit.c | 51 |
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); |
