mirror of
https://git.codelinaro.org/clo/qsdk/oss/boot/u-boot-2016.git
synced 2025-12-10 07:44:53 +01:00
Merge branch '2021-02-15-fix-CVE-2021-27097-CVE-2021-27138'
Fix CVE-2021-27097 and CVE-2021-27138. For more details see http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-27097 and http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-27138 Change-Id: I8f1f03a7e4b1d0bc9d8db51b6a08219f0c47fc2d Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
This commit is contained in:
parent
aea4f25d69
commit
af3ad1d289
18 changed files with 236 additions and 28 deletions
|
|
@ -717,3 +717,12 @@ config CONSOLE_RECORD_IN_SIZE
|
|||
tstc() and getc() will use this in preference to real device input.
|
||||
The buffer is allocated immediately after the malloc() region is
|
||||
ready.
|
||||
|
||||
config FIT_FULL_CHECK
|
||||
bool "Do a full check of the FIT before using it"
|
||||
default y
|
||||
help
|
||||
Enable this do a full check of the FIT to make sure it is valid. This
|
||||
helps to protect against carefully crafted FITs which take advantage
|
||||
of bugs or omissions in the code. This includes a bad structure,
|
||||
multiple root nodes and the like.
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ static int image_info(ulong addr)
|
|||
case IMAGE_FORMAT_FIT:
|
||||
puts(" FIT image found\n");
|
||||
|
||||
if (!fit_check_format(hdr)) {
|
||||
if (fit_check_format(hdr, IMAGE_SIZE_INVAL)) {
|
||||
puts("Bad FIT image format!\n");
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -352,7 +352,7 @@ static int do_imls_nor(void)
|
|||
#endif
|
||||
#if defined(CONFIG_FIT)
|
||||
case IMAGE_FORMAT_FIT:
|
||||
if (!fit_check_format(hdr))
|
||||
if (fit_check_format(hdr, IMAGE_SIZE_INVAL))
|
||||
goto next_sector;
|
||||
|
||||
printf("FIT Image at %08lX:\n", (ulong)hdr);
|
||||
|
|
@ -434,7 +434,7 @@ static int nand_imls_fitimage(nand_info_t *nand, int nand_dev, loff_t off,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (!fit_check_format(imgdata)) {
|
||||
if (fit_check_format(imgdata, IMAGE_SIZE_INVAL)) {
|
||||
free(imgdata);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ int common_diskboot(cmd_tbl_t *cmdtp, const char *intf, int argc,
|
|||
/* This cannot be done earlier,
|
||||
* we need complete FIT image in RAM first */
|
||||
if (fit_hdr && genimg_get_format((void *)addr) == IMAGE_FORMAT_FIT) {
|
||||
if (!fit_check_format(fit_hdr)) {
|
||||
if (fit_check_format(fit_hdr, IMAGE_SIZE_INVAL)) {
|
||||
bootstage_error(BOOTSTAGE_ID_IDE_FIT_READ);
|
||||
puts("** Bad FIT image format\n");
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -731,7 +731,7 @@ int do_fdcboot (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|||
#if defined(CONFIG_FIT)
|
||||
/* This cannot be done earlier, we need complete FIT image in RAM first */
|
||||
if (genimg_get_format ((void *)addr) == IMAGE_FORMAT_FIT) {
|
||||
if (!fit_check_format (fit_hdr)) {
|
||||
if (fit_check_format (fit_hdr, IMAGE_SIZE_INVAL)) {
|
||||
puts ("** Bad FIT image format\n");
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ int do_fpga(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (!fit_check_format(fit_hdr)) {
|
||||
if (fit_check_format(fit_hdr, IMAGE_SIZE_INVAL)) {
|
||||
puts("Bad FIT image format\n");
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -889,7 +889,7 @@ static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand,
|
|||
#if defined(CONFIG_FIT)
|
||||
/* This cannot be done earlier, we need complete FIT image in RAM first */
|
||||
if (fit_hdr && genimg_get_format((void *)addr) == IMAGE_FORMAT_FIT) {
|
||||
if (!fit_check_format(fit_hdr)) {
|
||||
if (fit_check_format(fit_hdr, IMAGE_SIZE_INVAL)) {
|
||||
bootstage_error(BOOTSTAGE_ID_NAND_FIT_READ);
|
||||
puts("** Bad FIT image format\n");
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ source (ulong addr, const char *fit_uname)
|
|||
}
|
||||
|
||||
fit_hdr = buf;
|
||||
if (!fit_check_format (fit_hdr)) {
|
||||
if (fit_check_format (fit_hdr, IMAGE_SIZE_INVAL)) {
|
||||
puts ("Bad FIT image format\n");
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ do_imgextract(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
|
|||
"at %08lx ...\n", uname, addr);
|
||||
|
||||
fit_hdr = (const void *)addr;
|
||||
if (!fit_check_format(fit_hdr)) {
|
||||
if (fit_check_format(fit_hdr, IMAGE_SIZE_INVAL)) {
|
||||
puts("Bad FIT image format\n");
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -353,7 +353,7 @@ int boot_get_fdt(int flag, int argc, char * const argv[], uint8_t arch,
|
|||
*/
|
||||
#if defined(CONFIG_FIT)
|
||||
/* check FDT blob vs FIT blob */
|
||||
if (fit_check_format(buf)) {
|
||||
if (!fit_check_format(buf, IMAGE_SIZE_INVAL)) {
|
||||
ulong load, len;
|
||||
|
||||
fdt_noffset = fit_image_load(images,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#define LOG_CATEGORY LOGC_BOOT
|
||||
#ifdef USE_HOSTCC
|
||||
#include "mkimage.h"
|
||||
#include <image.h>
|
||||
|
|
@ -991,11 +991,22 @@ int fit_image_verify(const void *fit, int image_noffset)
|
|||
{
|
||||
const void *data;
|
||||
size_t size;
|
||||
int noffset = 0;
|
||||
char *err_msg = "";
|
||||
int verify_all = 1;
|
||||
int ret;
|
||||
int noffset = 0;
|
||||
|
||||
#if defined(CONFIG_FIT_SIGNATURE)
|
||||
const char *name = fit_get_name(fit, image_noffset, NULL);
|
||||
if (strchr(name, '@')) {
|
||||
/*
|
||||
* We don't support this since libfdt considers names with the
|
||||
* name root but different @ suffix to be equal
|
||||
*/
|
||||
err_msg = "Node name contains @";
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
/* Get image data and data length */
|
||||
if (fit_image_get_data(fit, image_noffset, &data, &size)) {
|
||||
err_msg = "Can't get image data/size";
|
||||
|
|
@ -1192,6 +1203,33 @@ int fit_image_check_comp(const void *fit, int noffset, uint8_t comp)
|
|||
return (comp == image_comp);
|
||||
}
|
||||
|
||||
/**
|
||||
* fdt_check_no_at() - Check for nodes whose names contain '@'
|
||||
* This checks the parent node and all subnodes recursively
|
||||
* @fit: FIT to check
|
||||
* @parent: Parent node to check
|
||||
* @return 0 if OK, -EADDRNOTAVAIL is a node has a name containing '@'
|
||||
*/
|
||||
#if defined(CONFIG_FIT_SIGNATURE)
|
||||
static int fdt_check_no_at(const void *fit, int parent)
|
||||
{
|
||||
const char *name;
|
||||
int node;
|
||||
int ret;
|
||||
|
||||
name = fdt_get_name(fit, parent, NULL);
|
||||
if (!name || strchr(name, '@'))
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
fdt_for_each_subnode(fit, node, parent) {
|
||||
ret = fdt_check_no_at(fit, node);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* fit_check_format - sanity check FIT image format
|
||||
* @fit: pointer to the FIT format image header
|
||||
|
|
@ -1203,29 +1241,71 @@ int fit_image_check_comp(const void *fit, int noffset, uint8_t comp)
|
|||
* 1, on success
|
||||
* 0, on failure
|
||||
*/
|
||||
int fit_check_format(const void *fit)
|
||||
int fit_check_format(const void *fit, ulong size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = fdt_check_header(fit);
|
||||
if (ret) {
|
||||
debug("Wrong FIT format: not a flattened device tree\n");
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_FIT_FULL_CHECK)
|
||||
/*
|
||||
* If we are not given the size, make do wtih calculating it.
|
||||
* This is not as secure, so we should consider a flag to
|
||||
* control this.
|
||||
*/
|
||||
if (size == IMAGE_SIZE_INVAL)
|
||||
size = fdt_totalsize(fit);
|
||||
ret = fdt_check_full(fit, size);
|
||||
if (ret)
|
||||
ret = -EINVAL;
|
||||
|
||||
/*
|
||||
* U-Boot stopped using unit addressed in 2017. Since libfdt
|
||||
* can match nodes ignoring any unit address, signature
|
||||
* verification can see the wrong node if one is inserted with
|
||||
* the same name as a valid node but with a unit address
|
||||
* attached. Protect against this by disallowing unit addresses.
|
||||
*/
|
||||
#if defined(CONFIG_FIT_SIGNATURE)
|
||||
if (!ret)
|
||||
ret = fdt_check_no_at(fit, 0);
|
||||
|
||||
if (ret) {
|
||||
debug("FIT check error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (ret) {
|
||||
debug("FIT check error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
/* mandatory / node 'description' property */
|
||||
if (fdt_getprop(fit, 0, FIT_DESC_PROP, NULL) == NULL) {
|
||||
if (!fdt_getprop(fit, 0, FIT_DESC_PROP, NULL)) {
|
||||
debug("Wrong FIT format: no description\n");
|
||||
return 0;
|
||||
return -ENOMSG;
|
||||
}
|
||||
|
||||
if (IMAGE_ENABLE_TIMESTAMP) {
|
||||
/* mandatory / node 'timestamp' property */
|
||||
if (fdt_getprop(fit, 0, FIT_TIMESTAMP_PROP, NULL) == NULL) {
|
||||
if (!fdt_getprop(fit, 0, FIT_TIMESTAMP_PROP, NULL)) {
|
||||
debug("Wrong FIT format: no timestamp\n");
|
||||
return 0;
|
||||
return -ENODATA;
|
||||
}
|
||||
}
|
||||
|
||||
/* mandatory subimages parent '/images' node */
|
||||
if (fdt_path_offset(fit, FIT_IMAGES_PATH) < 0) {
|
||||
debug("Wrong FIT format: no images parent node\n");
|
||||
return 0;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1581,10 +1661,15 @@ int fit_image_load(bootm_headers_t *images, ulong addr,
|
|||
printf("## Loading %s from FIT Image at %08lx ...\n", prop_name, addr);
|
||||
|
||||
bootstage_mark(bootstage_id + BOOTSTAGE_SUB_FORMAT);
|
||||
if (!fit_check_format(fit)) {
|
||||
printf("Bad FIT %s image format!\n", prop_name);
|
||||
ret = fit_check_format(fit, IMAGE_SIZE_INVAL);
|
||||
if (ret) {
|
||||
printf("Bad FIT %s image format! (err=%d)\n", prop_name, ret);
|
||||
#if defined(CONFIG_FIT_SIGNATURE)
|
||||
if (ret == -EADDRNOTAVAIL)
|
||||
printf("Signature checking prevents use of unit addresses (@) in nodes\n");
|
||||
#endif
|
||||
bootstage_error(bootstage_id + BOOTSTAGE_SUB_FORMAT);
|
||||
return -ENOEXEC;
|
||||
return ret;
|
||||
}
|
||||
bootstage_mark(bootstage_id + BOOTSTAGE_SUB_FORMAT_OK);
|
||||
if (fit_uname) {
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ int update_tftp(ulong addr, char *interface, char *devstring)
|
|||
got_update_file:
|
||||
fit = (void *)addr;
|
||||
|
||||
if (!fit_check_format((void *)fit)) {
|
||||
if (fit_check_format((void *)fit, IMAGE_SIZE_INVAL)) {
|
||||
printf("Bad FIT format of the update file, aborting "
|
||||
"auto-update\n");
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ int debug_server_parse_firmware_fit_image(const void **raw_image_addr,
|
|||
goto out_error;
|
||||
}
|
||||
|
||||
if (!fit_check_format(fit_hdr)) {
|
||||
if (fit_check_format(fit_hdr, IMAGE_SIZE_INVAL)) {
|
||||
printf("Debug Server FW: Bad FIT image format\n");
|
||||
goto out_error;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ int parse_mc_firmware_fit_image(u64 mc_fw_addr,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!fit_check_format(fit_hdr)) {
|
||||
if (fit_check_format(fit_hdr, IMAGE_SIZE_INVAL)) {
|
||||
printf("fsl-mc: ERR: Bad firmware image (bad FIT header)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -792,6 +792,9 @@ int bootz_setup(ulong image, ulong *start, ulong *end);
|
|||
|
||||
#define FIT_MAX_HASH_LEN HASH_MAX_DIGEST_SIZE
|
||||
|
||||
/* An invalid size, meaning that the image size is not known */
|
||||
#define IMAGE_SIZE_INVAL (-1UL)
|
||||
|
||||
/* cmdline argument format parsing */
|
||||
int fit_parse_conf(const char *spec, ulong addr_curr,
|
||||
ulong *addr, const char **conf_name);
|
||||
|
|
@ -888,10 +891,26 @@ int fit_image_check_os(const void *fit, int noffset, uint8_t os);
|
|||
int fit_image_check_arch(const void *fit, int noffset, uint8_t arch);
|
||||
int fit_image_check_type(const void *fit, int noffset, uint8_t type);
|
||||
int fit_image_check_comp(const void *fit, int noffset, uint8_t comp);
|
||||
int fit_check_format(const void *fit);
|
||||
/**
|
||||
* fit_check_format() - Check that the FIT is valid
|
||||
*
|
||||
* This performs various checks on the FIT to make sure it is suitable for
|
||||
* use, looking for mandatory properties, nodes, etc.
|
||||
*
|
||||
* If FIT_FULL_CHECK is enabled, it also runs it through libfdt to make
|
||||
* sure that there are no strange tags or broken nodes in the FIT.
|
||||
*
|
||||
* @fit: pointer to the FIT format image header
|
||||
* @return 0 if OK, -ENOEXEC if not an FDT file, -EINVAL if the full FDT check
|
||||
* failed (e.g. due to bad structure), -ENOMSG if the description is
|
||||
* missing, -ENODATA if the timestamp is missing, -ENOENT if the /images
|
||||
* path is missing
|
||||
*/
|
||||
int fit_check_format(const void *fit, ulong size);
|
||||
|
||||
int fit_conf_find_compat(const void *fit, const void *fdt);
|
||||
int fit_conf_get_node(const void *fit, const char *conf_uname);
|
||||
int fdt_check_full(const void *fdt, size_t bufsize);
|
||||
|
||||
/**
|
||||
* fit_conf_get_prop_node() - Get node refered to by a configuration
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
#define INT_MAX ((int)(~0U>>1))
|
||||
static int _fdt_nodename_eq(const void *fdt, int offset,
|
||||
const char *s, int len)
|
||||
{
|
||||
|
|
@ -625,3 +626,83 @@ int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
|
|||
|
||||
return offset; /* error from fdt_next_node() */
|
||||
}
|
||||
|
||||
int fdt_check_full(const void *fdt, size_t bufsize)
|
||||
{
|
||||
int err;
|
||||
int num_memrsv;
|
||||
int offset, nextoffset = 0;
|
||||
uint32_t tag;
|
||||
unsigned depth = 0;
|
||||
const void *prop;
|
||||
const char *propname;
|
||||
bool expect_end = false;
|
||||
|
||||
if (bufsize < FDT_V1_SIZE)
|
||||
return -FDT_ERR_TRUNCATED;
|
||||
err = fdt_check_header(fdt);
|
||||
if (err != 0)
|
||||
return err;
|
||||
if (bufsize < fdt_totalsize(fdt))
|
||||
return -FDT_ERR_TRUNCATED;
|
||||
|
||||
num_memrsv = fdt_num_mem_rsv(fdt);
|
||||
if (num_memrsv < 0)
|
||||
return num_memrsv;
|
||||
|
||||
while (1) {
|
||||
offset = nextoffset;
|
||||
tag = fdt_next_tag(fdt, offset, &nextoffset);
|
||||
|
||||
if (nextoffset < 0)
|
||||
return nextoffset;
|
||||
|
||||
/* If we see two root nodes, something is wrong */
|
||||
if (expect_end && tag != FDT_END)
|
||||
return -FDT_ERR_BADLAYOUT;
|
||||
|
||||
switch (tag) {
|
||||
case FDT_NOP:
|
||||
break;
|
||||
|
||||
case FDT_END:
|
||||
if (depth != 0)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
return 0;
|
||||
|
||||
case FDT_BEGIN_NODE:
|
||||
depth++;
|
||||
if (depth > INT_MAX)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
|
||||
/* The root node must have an empty name */
|
||||
if (depth == 1) {
|
||||
const char *name;
|
||||
int len;
|
||||
|
||||
name = fdt_get_name(fdt, offset, &len);
|
||||
if (*name || len)
|
||||
return -FDT_ERR_BADLAYOUT;
|
||||
}
|
||||
break;
|
||||
|
||||
case FDT_END_NODE:
|
||||
if (depth == 0)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
depth--;
|
||||
if (depth == 0)
|
||||
expect_end = true;
|
||||
break;
|
||||
|
||||
case FDT_PROP:
|
||||
prop = fdt_getprop_by_offset(fdt, offset, &propname,
|
||||
&err);
|
||||
if (!prop)
|
||||
return err;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -FDT_ERR_INTERNAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ int fdt_find_regions(const void *fdt, char * const inc[], int inc_count,
|
|||
int depth = -1;
|
||||
int want = 0;
|
||||
int base = fdt_off_dt_struct(fdt);
|
||||
bool expect_end = false;
|
||||
|
||||
end = path;
|
||||
*end = '\0';
|
||||
|
|
@ -119,6 +120,10 @@ int fdt_find_regions(const void *fdt, char * const inc[], int inc_count,
|
|||
tag = fdt_next_tag(fdt, offset, &nextoffset);
|
||||
stop_at = nextoffset;
|
||||
|
||||
/* If we see two root nodes, something is wrong */
|
||||
if (expect_end && tag != FDT_END)
|
||||
return -FDT_ERR_BADLAYOUT;
|
||||
|
||||
switch (tag) {
|
||||
case FDT_PROP:
|
||||
include = want >= 2;
|
||||
|
|
@ -139,6 +144,9 @@ int fdt_find_regions(const void *fdt, char * const inc[], int inc_count,
|
|||
if (depth == FDT_MAX_DEPTH)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
name = fdt_get_name(fdt, offset, &len);
|
||||
/* The root node must have an empty name */
|
||||
if (!depth && *name)
|
||||
return -FDT_ERR_BADLAYOUT;
|
||||
if (end - path + 2 + len >= path_len)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
if (end != path + 1)
|
||||
|
|
@ -163,6 +171,8 @@ int fdt_find_regions(const void *fdt, char * const inc[], int inc_count,
|
|||
while (end > path && *--end != '/')
|
||||
;
|
||||
*end = '\0';
|
||||
if (depth == -1)
|
||||
expect_end = true;
|
||||
break;
|
||||
|
||||
case FDT_END:
|
||||
|
|
|
|||
|
|
@ -27,7 +27,11 @@
|
|||
int fit_verify_header(unsigned char *ptr, int image_size,
|
||||
struct image_tool_params *params)
|
||||
{
|
||||
return fdt_check_header(ptr);
|
||||
if (fdt_check_header(ptr) != EXIT_SUCCESS ||
|
||||
fit_check_format(ptr, IMAGE_SIZE_INVAL))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int fit_check_image_types(uint8_t type)
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ static int fit_extract_contents(void *ptr, struct image_tool_params *params)
|
|||
/* Indent string is defined in header image.h */
|
||||
p = IMAGE_INDENT_STRING;
|
||||
|
||||
if (!fit_check_format(fit)) {
|
||||
if (fit_check_format(fit, IMAGE_SIZE_INVAL)) {
|
||||
printf("Bad FIT image format\n");
|
||||
return -1;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue