Merge "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"

This commit is contained in:
Linux Build Service Account 2022-11-28 01:46:31 -08:00 committed by Gerrit - the friendly Code Review server
commit 9b9aa73681
18 changed files with 236 additions and 28 deletions

View file

@ -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.

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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,

View file

@ -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) {

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}
}
}

View file

@ -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:

View file

@ -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)

View file

@ -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;
}