summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAristo Chen <aristo.chen@canonical.com>2026-08-18 13:23:15 +0000
committerTom Rini <trini@konsulko.com>2026-08-18 14:36:57 -0600
commit0a2e329b829d905889aa9bad0c9f4d28cc302ab1 (patch)
treeecb9d5bab67fd592be7873034db3632624d34163
parent41541f47a3af93d76d63c2d6d1f1c859fa3dd61a (diff)
downloadu-boot-0a2e329b829d905889aa9bad0c9f4d28cc302ab1.tar.gz
u-boot-0a2e329b829d905889aa9bad0c9f4d28cc302ab1.zip
bootm: size the noload gzip decompression buffer from ISIZE
For a compressed kernel_noload image, bootm_load_os() allocates a per-image decompression buffer of ALIGN(image_len * 8, SZ_1M). The 8x multiplier is a heuristic that comfortably covers what zstd and xz achieve on real kernels, but a highly compressible payload (say, a run of zeros) can exceed it and fail decompression. gzip carries the original size in the last 4 bytes of the stream (ISIZE, modulo 2^32). Where the compressed image is a gzip stream, read ISIZE and use ALIGN(hdr_size, SZ_1M) as the buffer, capped at CONFIG_SYS_BOOTM_LEN because the value is attacker-controlled. For non-gzip streams or when ISIZE cannot be trusted, fall back to the existing 8x multiplier. The size read is done via a small static helper in bootm.c, wired up via a switch on os.comp so the same pattern can be extended to other formats without adding a new public interface. The other formats U-Boot supports (lzma, lz4, zstd) also carry a size hint and are added in follow-up patches. Suggested-by: Simon Glass <sjg@chromium.org> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
-rw-r--r--boot/bootm.c47
1 files changed, 42 insertions, 5 deletions
diff --git a/boot/bootm.c b/boot/bootm.c
index 3bce8586834..ab787979f2e 100644
--- a/boot/bootm.c
+++ b/boot/bootm.c
@@ -23,6 +23,7 @@
#include <asm/cache.h>
#include <asm/global_data.h>
#include <asm/io.h>
+#include <asm/unaligned.h>
#include <linux/sizes.h>
#include <tpm-v2.h>
#include <tpm_tcg2.h>
@@ -638,6 +639,24 @@ static int handle_decomp_error(int comp_type, size_t uncomp_size,
#endif
#ifndef USE_HOSTCC
+#if CONFIG_IS_ENABLED(GZIP)
+/*
+ * Return the gzip stream's uncompressed size from its ISIZE trailer, or
+ * 0 if the buffer is not a gzip stream. Only the two magic bytes are
+ * checked, since a fuller validation happens inside gunzip() during
+ * decompression; the caller uses the return value as a size hint only.
+ */
+static ulong bootm_gzip_uncompressed_size(const void *src, ulong len)
+{
+ const u8 *b = src;
+
+ /* Minimum gzip: 10-byte header + 2-byte deflate + 8-byte trailer */
+ if (len < 20 || b[0] != 0x1f || b[1] != 0x8b)
+ return 0;
+ return get_unaligned_le32(b + len - 4);
+}
+#endif
+
static int bootm_load_os(struct bootm_headers *images, int boot_progress)
{
const struct image_info os = images->os;
@@ -654,17 +673,36 @@ static int bootm_load_os(struct bootm_headers *images, int boot_progress)
void *load_buf, *image_buf;
int err;
+ image_buf = map_sysmem(os.image_start, image_len);
+
/*
* 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.
- * 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
+ * For a gzip stream the trailing 4-byte ISIZE field holds the
+ * original size modulo 2^32; when it is present and within
+ * CONFIG_SYS_BOOTM_LEN, allocate exactly that. Otherwise fall back
+ * to an 8x multiplier, which 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;
+ ulong hdr_size = 0;
- decomp_len = ALIGN(image_len * 8, SZ_1M);
+ switch (os.comp) {
+#if CONFIG_IS_ENABLED(GZIP)
+ case IH_COMP_GZIP:
+ hdr_size = bootm_gzip_uncompressed_size(image_buf,
+ image_len);
+ break;
+#endif
+ default:
+ break;
+ }
+ if (hdr_size && hdr_size <= CONFIG_SYS_BOOTM_LEN)
+ decomp_len = ALIGN(hdr_size, SZ_1M);
+ else
+ decomp_len = ALIGN(image_len * 8, SZ_1M);
decomp_limit = BOOTM_DECOMP_LIMIT_PER_IMAGE;
err = lmb_alloc_mem(LMB_MEM_ALLOC_ANY, SZ_2M, &addr,
decomp_len, LMB_NONE);
@@ -679,7 +717,6 @@ static int bootm_load_os(struct bootm_headers *images, int boot_progress)
}
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,
decomp_len, &load_end);