protos/linux_risc: Validate kernel header before trusting image_size for allocation
diff --git a/common/protos/linux_risc.c b/common/protos/linux_risc.c
index 5b1951d8..c1c09567 100644
--- a/common/protos/linux_risc.c
+++ b/common/protos/linux_risc.c
@@ -95,9 +95,7 @@ struct boot_param {
#define LINUX_HEADER_MAGIC2 0x818223cd
#endif
-static const char *verify_kernel(struct boot_param *p) {
- struct linux_header *header = p->kernel_base;
-
+static const char *verify_kernel(struct linux_header *header) {
if (header->magic2 != LINUX_HEADER_MAGIC2) {
return "kernel header magic does not match";
}
@@ -436,15 +434,24 @@ noreturn void linux_load(char *config, char *cmdline) {
}
p.kernel_size = kernel_file->size;
+
+ if (p.kernel_size < sizeof(struct linux_header)) {
+ panic(true, "linux: kernel too small to contain a valid header");
+ }
+
+ struct linux_header tmp_hdr;
+ fread(kernel_file, &tmp_hdr, 0, sizeof(tmp_hdr));
+
+ const char *reason = verify_kernel(&tmp_hdr);
+ if (reason)
+ panic(true, "linux: invalid kernel image: %s", reason);
+
+ // Use image_size from kernel header for total memory including BSS
size_t kernel_alloc_size = p.kernel_size;
- // Read image_size from kernel header for total memory including BSS
- if (p.kernel_size >= sizeof(struct linux_header)) {
- struct linux_header tmp_hdr;
- fread(kernel_file, &tmp_hdr, 0, sizeof(tmp_hdr));
- if (tmp_hdr.image_size > kernel_alloc_size) {
- kernel_alloc_size = tmp_hdr.image_size;
- }
+ if (tmp_hdr.image_size > kernel_alloc_size) {
+ kernel_alloc_size = tmp_hdr.image_size;
}
+
p.kernel_base = ext_mem_alloc_type_aligned(
ALIGN_UP(kernel_alloc_size, 4096),
MEMMAP_KERNEL_AND_MODULES, 2 * 1024 * 1024);
@@ -452,10 +459,6 @@ noreturn void linux_load(char *config, char *cmdline) {
fclose(kernel_file);
printv("linux: loaded kernel `%s` at %p, size %U\n", kernel_path, p.kernel_base, (uint64_t)p.kernel_size);
- const char *reason = verify_kernel(&p);
- if (reason)
- panic(true, "linux: invalid kernel image: %s", reason);
-
load_module(&p, config);
prepare_device_tree_blob(&p);
