From: Binarly Vulnerability Research <[email protected]>
The fit_config_check_sig() function in boot/image-fit-sig.c builds a
list of FDT regions that cover the parts of the FIT that must be
hashed for the signature verification. Once the structure regions
have been collected, the function adds one extra region for the
string table, using the 'hashed-strings' property:
/* Add the strings */
strings = fdt_getprop(fit, noffset, "hashed-strings", NULL);
if (strings) {
/*
* The strings region offset must be a static 0x0.
* This is set in tool/image-host.c
*/
fdt_regions[count].offset = fdt_off_dt_strings(fit);
fdt_regions[count].size = fdt32_to_cpu(strings[1]);
count++;
}
/* Allocate the region list on the stack */
struct image_region region[count];
fit_region_make_list(fit, fdt_regions, count, region);
if (info.crypto->verify(&info, region, count, fit_value,
fit_value_len)) {
*err_msgp = "Verification failed";
return -1;
}
'strings' is a pointer to the raw bytes of the 'hashed-strings'
property in the FIT. No check is performed on strings[1], and it is
assigned verbatim as the size of the strings region. The
fit_region_make_list() function then copies the size verbatim into
the 'region' array:
for (i = 0; i < count; i++) {
debug("%10x %10x\n", fdt_regions[i].offset,
fdt_regions[i].size);
region[i].data = fit + fdt_regions[i].offset;
region[i].size = fdt_regions[i].size;
}
info.crypto->verify for 'sha1,rsa2048' is rsa_verify(), located in
lib/rsa/rsa-verify.c, which calls hash_calculate() defined in
lib/hash-checksum.c:
for (i = 0; i < region_count - 1; i++) {
ret = algo->hash_update(algo, ctx, region[i].data,
region[i].size, 0);
if (ret)
return ret;
}
hash_update_sha1(), defined in common/hash.c, takes the per-region
size as an unsigned int and feeds it directly to sha1_update(). With
an attacker-chosen value such as 0xFFFFFFFF, the SHA update reads
nearly four gigabytes from fit + fdt_off_dt_strings(fit), walking
well past the end of the FIT image and into unmapped memory.
Fix: Validate that the declared size is non-negative and fits within
the FIT before adding the strings region to the list. The offset
itself is already validated by fdt_check_header().
Signed-off-by: Binarly Vulnerability Research <[email protected]>
---
boot/image-fit-sig.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/boot/image-fit-sig.c b/boot/image-fit-sig.c
index 433df20281f..bdfb5e3eb7c 100644
--- a/boot/image-fit-sig.c
+++ b/boot/image-fit-sig.c
@@ -512,8 +512,18 @@ static int fit_config_check_sig(const void *fit, int
noffset, int conf_noffset,
* The strings region offset must be a static 0x0.
* This is set in tool/image-host.c
*/
- fdt_regions[count].offset = fdt_off_dt_strings(fit);
- fdt_regions[count].size = fdt32_to_cpu(strings[1]);
+ int offset = fdt_off_dt_strings(fit);
+ int size = fdt32_to_cpu(strings[1]);
+ /*
+ * The offset should be already validated by fdt_check_header();
+ * validate the size here.
+ */
+ if (size < 0 || size > fdt_totalsize(fit) - offset) {
+ *err_msgp = "Strings region is out of bounds";
+ return -1;
+ }
+ fdt_regions[count].offset = offset;
+ fdt_regions[count].size = size;
count++;
}
--
2.53.0