From: Ana Bobillo <[email protected]>

zimage_get_kernel_version() finds the version string at an offset of up
to 64KB from the kernel base, taken from the kernel_version field of the
setup header, and then scans it up to its nul terminator. It works out a
256-byte limit for the scan but never checks it, and it does not know how
much of the image is there to read.

This matters for the ChromiumOS bootmeth, which reads the kernel setup
data from disk into a buffer of bootloader_size + 8KB bytes and passes a
kernel base 24KB into that buffer. With a bootloader of less than 16KB,
the kernel base is already past the end of the buffer, and the header
fields come from the disk.

Pass zimage_get_kernel_version() the number of bytes it may read at the
kernel base, and stop the scan at the 256-byte limit. The zboot code
passes the size of the setup code, which holds the string according to
the boot protocol, and the ChromiumOS bootmeth passes what is left of its
buffer.

A version string longer than 255 characters is no longer shown; Linux's
are around 100.

Fixes: 7c79eddbec32 ("x86: zimage: Sanity-check the kernel version before 
printing it")
Fixes: f861b1ee70aa ("bootstd: cros: Add a function to read info from 
partition")
Signed-off-by: Ana Bobillo <[email protected]>
---
(no changes since v1)

 arch/x86/lib/zimage.c | 27 ++++++++++++++++++---------
 boot/bootmeth_cros.c  |  7 ++++++-
 include/bootm.h       |  6 ++++--
 3 files changed, 28 insertions(+), 12 deletions(-)

diff --git a/arch/x86/lib/zimage.c b/arch/x86/lib/zimage.c
index a5f2231aa52..53c8e0429e4 100644
--- a/arch/x86/lib/zimage.c
+++ b/arch/x86/lib/zimage.c
@@ -145,11 +145,12 @@ static int setup_device_tree(struct setup_header *hdr, 
const void *fdt_blob)
 }
 
 const char *zimage_get_kernel_version(struct boot_params *params,
-                                     void *kernel_base)
+                                     void *kernel_base, ulong size)
 {
        struct setup_header *hdr = &params->hdr;
-       int bootproto;
        const char *s, *end;
+       ulong offset;
+       int bootproto;
 
        bootproto = get_boot_protocol(hdr, false);
        log_debug("bootproto %x, hdr->setup_sects %x\n", bootproto,
@@ -157,16 +158,23 @@ const char *zimage_get_kernel_version(struct boot_params 
*params,
        if (bootproto < 0x0200 || hdr->setup_sects < 15)
                return NULL;
 
-       /* sanity-check the kernel version in case it is missing */
+       /* the offset comes from the image, so check it is within @size */
+       offset = hdr->kernel_version + 0x200;
        log_debug("hdr->kernel_version %x, str at %p\n", hdr->kernel_version,
-                 kernel_base + hdr->kernel_version + 0x200);
-       for (s = kernel_base + hdr->kernel_version + 0x200, end = s + 0x100; *s;
-            s++) {
+                 kernel_base + offset);
+       if (offset >= size)
+               return NULL;
+
+       /* sanity-check the kernel version in case it is missing */
+       end = kernel_base + min(size, offset + 0x100);
+       for (s = kernel_base + offset; s < end && *s; s++) {
                if (!isprint(*s))
                        return NULL;
        }
+       if (s == end)
+               return NULL;
 
-       return kernel_base + hdr->kernel_version + 0x200;
+       return kernel_base + offset;
 }
 
 struct boot_params *load_zimage(char *image, unsigned long kernel_size,
@@ -206,7 +214,7 @@ struct boot_params *load_zimage(char *image, unsigned long 
kernel_size,
        log_debug("Using boot protocol version %x.%02x\n",
                  (bootproto & 0xff00) >> 8, bootproto & 0xff);
 
-       version = zimage_get_kernel_version(params, image);
+       version = zimage_get_kernel_version(params, image, setup_size);
        if (version)
                printf("Linux kernel version %s\n", version);
        else
@@ -577,7 +585,8 @@ void zimage_dump(struct boot_params *base_ptr, bool 
show_cmdline)
        print_num("Start sys seg", hdr->start_sys_seg);
        print_num("Kernel version", hdr->kernel_version);
        version = zimage_get_kernel_version(base_ptr,
-                                           (void *)state.bzimage_addr);
+                                           (void *)state.bzimage_addr,
+                                           (hdr->setup_sects + 1) * 512);
        if (version)
                printf("   @%p: %s\n", version, version);
        print_num("Type of loader", hdr->type_of_loader);
diff --git a/boot/bootmeth_cros.c b/boot/bootmeth_cros.c
index 805680f71ad..006f30c0bb4 100644
--- a/boot/bootmeth_cros.c
+++ b/boot/bootmeth_cros.c
@@ -225,9 +225,14 @@ static int cros_read_buf(struct bootflow *bflow, void 
*buf, ulong size,
 
 #ifdef CONFIG_X86
        const char *version;
+       ulong kern_size = 0;
 
+       /* The kernel base may lie beyond the end of the buffer */
+       if (size > before_base + X86_KERNEL_OFFSET)
+               kern_size = size - before_base - X86_KERNEL_OFFSET;
        version = zimage_get_kernel_version(map_sysmem(setup, 0),
-                                           map_sysmem(kern_base, 0));
+                                           map_sysmem(kern_base, 0),
+                                           kern_size);
        log_debug("version %s\n", version);
        if (version)
                bflow->name = strdup(version);
diff --git a/include/bootm.h b/include/bootm.h
index d1aac3d44df..5b202cf2d97 100644
--- a/include/bootm.h
+++ b/include/bootm.h
@@ -299,10 +299,12 @@ int zboot_run(ulong addr, ulong size, ulong initrd, ulong 
initrd_size,
  *
  * @params: boot_params pointer
  * @kernel_base: base address of kernel
- * Return: Kernel version as a NUL-terminated string
+ * @size: Number of bytes at @kernel_base which may be read
+ * Return: Kernel version as a NUL-terminated string, or NULL if there is no
+ *     valid version string within @size bytes of @kernel_base
  */
 const char *zimage_get_kernel_version(struct boot_params *params,
-                                     void *kernel_base);
+                                     void *kernel_base, ulong size);
 
 /**
  * zimage_dump() - Dump the metadata of a zimage

Reply via email to