vbe_read_fit() loads a firmware-phase FIT from the trusted firmware area and then issues a blk_read() to pull in the image, and optionally an FDT, referenced by the FIT image node. The source offset on the device and the read length both come from the FIT's data-position or data-offset property and its data-size property, which live on mutable boot media and can be controlled by an attacker with prior write access to the firmware area.
Without a range check the resulting blk_read() can read past the firmware area on the device and, on the non-SPL path, write an attacker-chosen number of blocks past the malloc(aligned_size) FIT buffer into adjacent memory. Only the SPL branch routes through spl_load_simple_fit(), which hashes the data. The external-data block reached from TPL or VPL, and from the bootflow path via abrec_read_bootflow_fw() and vbe_simple_read_bootflow_fw(), runs before any signature or hash check on the loaded phase. Confine the FIT-supplied [load_addr, load_addr + len) window to [addr, addr + area_size] before computing block numbers and lengths, and apply the same constraint to fdt_load_addr and fdt_size. The checks are written in subtraction-only form against the trusted area_size so the comparison itself cannot overflow. Signed-off-by: Aristo Chen <[email protected]> --- boot/vbe_common.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/boot/vbe_common.c b/boot/vbe_common.c index a86986d86e9..844100b7ee6 100644 --- a/boot/vbe_common.c +++ b/boot/vbe_common.c @@ -278,6 +278,17 @@ int vbe_read_fit(struct udevice *blk, ulong area_offset, ulong area_size, ulong fdt_offset; void *base_buf, *fdt_base_buf; + /* + * load_addr and len describe the external data location in + * the FIT, which lives on mutable boot media. Bound them + * against the trusted area_size so the blk_read() below + * cannot read past the firmware area or write an + * attacker-chosen number of blocks past the FIT allocation. + */ + if (load_addr < addr || load_addr - addr > area_size || + len > area_size - (load_addr - addr)) + return log_msg_ret("rng", -E2BIG); + /* Find the start address to load from */ base = ALIGN_DOWN(load_addr, desc->blksz); @@ -321,6 +332,11 @@ int vbe_read_fit(struct udevice *blk, ulong area_offset, ulong area_size, /* now the FDT */ if (fdt_size) { + /* Same constraint as above for the FDT region */ + if (fdt_load_addr < addr || + fdt_load_addr - addr > area_size || + fdt_size > area_size - (fdt_load_addr - addr)) + return log_msg_ret("rng", -E2BIG); fdt_offset = area_offset + fdt_load_addr - addr; blknum = fdt_offset / desc->blksz; extra = fdt_offset % desc->blksz; -- 2.43.0

