From 2ff26c1e37858ba0b2fd12c82e114a3cedcb317a Mon Sep 17 00:00:00 2001 From: Aristo Chen Date: Fri, 5 Jun 2026 15:42:49 +0000 Subject: bootm: fix overflow of the noload kernel decompression buffer For a compressed kernel_noload image, bootm_load_os() allocates a decompression buffer sized to ALIGN(image_len * 4, SZ_1M), assuming the kernel compresses by no more than a factor of four. It then passes CONFIG_SYS_BOOTM_LEN, rather than the size of that buffer, to image_decomp() as the output limit. The decompressors honour the limit they are given, so a kernel that decompresses to more than four times its compressed size is written past the end of the allocated buffer and corrupts adjacent memory. Pass the allocation size to image_decomp() and handle_decomp_error() so decompression stops at the buffer boundary and fails cleanly when the image is too large, instead of overflowing. The regular non-noload paths are unchanged and continue to use CONFIG_SYS_BOOTM_LEN. When the failure is triggered by the smaller per-image buffer, print a note so that handle_decomp_error()'s generic advice to increase CONFIG_SYS_BOOTM_LEN does not mislead the reader. Fixes: 69544c4fd8b1 ("bootm: Support kernel_noload with compression") Reviewed-by: Simon Glass Signed-off-by: Aristo Chen --- boot/bootm.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'boot') diff --git a/boot/bootm.c b/boot/bootm.c index 4836d6b2d41..b55c41f30b7 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -610,6 +610,7 @@ static int bootm_load_os(struct bootm_headers *images, int boot_progress) ulong blob_end = os.end; ulong image_start = os.image_start; ulong image_len = os.image_len; + ulong decomp_len = CONFIG_SYS_BOOTM_LEN; ulong flush_start = ALIGN_DOWN(load, ARCH_DMA_MINALIGN); bool no_overlap; void *load_buf, *image_buf; @@ -623,11 +624,11 @@ static int bootm_load_os(struct bootm_headers *images, int boot_progress) * Use an alignment of 2MB since this might help arm64 */ if (os.type == IH_TYPE_KERNEL_NOLOAD && os.comp != IH_COMP_NONE) { - ulong req_size = ALIGN(image_len * 4, SZ_1M); phys_addr_t addr; + decomp_len = ALIGN(image_len * 4, SZ_1M); err = lmb_alloc_mem(LMB_MEM_ALLOC_ANY, SZ_2M, &addr, - req_size, LMB_NONE); + decomp_len, LMB_NONE); if (err) return 1; @@ -635,17 +636,20 @@ static int bootm_load_os(struct bootm_headers *images, int boot_progress) images->os.load = (ulong)addr; images->ep = (ulong)addr; debug("Allocated %lx bytes at %lx for kernel (size %lx) decompression\n", - req_size, load, image_len); + decomp_len, load, image_len); } load_buf = map_sysmem(load, 0); image_buf = map_sysmem(os.image_start, image_len); err = image_decomp(os.comp, load, os.image_start, os.type, load_buf, image_buf, image_len, - CONFIG_SYS_BOOTM_LEN, &load_end); + decomp_len, &load_end); if (err) { err = handle_decomp_error(os.comp, load_end - load, - CONFIG_SYS_BOOTM_LEN, err); + decomp_len, err); + if (os.type == IH_TYPE_KERNEL_NOLOAD && os.comp != IH_COMP_NONE) + printf("Note: noload decompression buffer is %#lx bytes (not CONFIG_SYS_BOOTM_LEN)\n", + decomp_len); bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); return err; } -- cgit v1.3.1 From 4956f108539135afb1afdec2b509f62291087d16 Mon Sep 17 00:00:00 2001 From: Aristo Chen Date: Fri, 5 Jun 2026 15:42:50 +0000 Subject: bootm: increase kernel_noload decompression headroom from 4x to 8x For a compressed kernel_noload image, bootm_load_os() allocates a buffer of ALIGN(image_len * 4, SZ_1M). The 4x factor is at the edge of what modern compressors (zstd, xz) achieve on real kernels, so a well-compressed vendor kernel can fail to boot at runtime with no intervening warning. Bump the headroom to 8x. The buffer is still bounded by the compressed image size, and the SZ_1M alignment keeps the overhead below 1 MiB on small kernels. Suggested-by: Simon Glass Signed-off-by: Aristo Chen --- boot/bootm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'boot') diff --git a/boot/bootm.c b/boot/bootm.c index b55c41f30b7..4eee35d7e62 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -619,14 +619,14 @@ static int bootm_load_os(struct bootm_headers *images, int boot_progress) /* * For a "noload" compressed kernel we need to allocate a buffer large * enough to decompress in to and use that as the load address now. - * Assume that the kernel compression is at most a factor of 4 since - * zstd almost achieves that. + * Allow up to 8x compression: this comfortably covers what zstd and xz + * achieve on real kernels, with headroom for well-compressed payloads. * Use an alignment of 2MB since this might help arm64 */ if (os.type == IH_TYPE_KERNEL_NOLOAD && os.comp != IH_COMP_NONE) { phys_addr_t addr; - decomp_len = ALIGN(image_len * 4, SZ_1M); + decomp_len = ALIGN(image_len * 8, SZ_1M); err = lmb_alloc_mem(LMB_MEM_ALLOC_ANY, SZ_2M, &addr, decomp_len, LMB_NONE); if (err) -- cgit v1.3.1