kvmtool, on arm and arm64, puts the kernel, DTB and initrd (if present) in a 256MB memory region that starts at the bottom of RAM. kvm__arch_load_kernel_image() copies the kernel at the start of RAM, the DTB is placed at the top of the region, and immediately below it the initrd. When the initrd is specified by the user, kvmtool checks that it doesn't overlap with the kernel by computing the start address in the host's address space: fstat(fd_initrd, &sb); pos = pos - (sb.st_size + INITRD_ALIGN); guest_addr = ALIGN(host_to_guest_flat(kvm, pos), INITRD_ALIGN); (a) pos = guest_flat_to_host(kvm, guest_addr); (b) If the initrd is large enough to completely overwrite the kernel and start below the guest RAM (pos < kvm->ram_start), then kvmtool will omit the following errors: Warning: unable to translate host address 0xfffe849ffffc to guest (1) Warning: unable to translate guest address 0x0 to host (2) Fatal: initrd overlaps with kernel image. (3) (1) is because (a) calls host_to_guest_flat(kvm, pos) with a 'pos' outside any of the memslots. (2) is because guest_flat_to_host() is called at (b) with guest_addr=0, which is what host_to_guest_flat() returns if the supplied address is not found in any of the memslots. This warning is eliminated by this patch. And finally, (3) is the most useful message, because it tells the user what the error is. The issue is a more general pattern in kvm__arch_load_kernel_image(): kvmtool doesn't check if host_to_guest_flat() returns 0, which means that the host address is not within any of the memslots. Add a check for that, which will at the very least remove the second warning. This also fixes the following edge cases: 1. The same warnings being emitted in a similar scenario with the DTB, when the RAM is smaller than FDT_MAX_SIZE + FDT_ALIGN. 2. When copying the kernel, if the RAM size is smaller than the kernel offset, the start of the kernel (represented by the variable 'pos') will be outside the VA space allocated for the guest RAM. limit - pos will wrap around, because gcc 14.1.1 wraps the pointers (void pointer arithmetic is undefined in C99). Then read_file()->..->read() will return -EFAULT because the destination address is unallocated (as per man 2 read, also reproduced during testing). Signed-off-by: Alexandru Elisei <alexandru.elisei@xxxxxxx> --- arm/kvm.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/arm/kvm.c b/arm/kvm.c index da0430c40c36..4beae69e1fb3 100644 --- a/arm/kvm.c +++ b/arm/kvm.c @@ -113,6 +113,8 @@ bool kvm__arch_load_kernel_image(struct kvm *kvm, int fd_kernel, int fd_initrd, pos = kvm->ram_start + kvm__arch_get_kern_offset(kvm, fd_kernel); kvm->arch.kern_guest_start = host_to_guest_flat(kvm, pos); + if (!kvm->arch.kern_guest_start) + die("guest memory too small to contain the kernel"); file_size = read_file(fd_kernel, pos, limit - pos); if (file_size < 0) { if (errno == ENOMEM) @@ -131,7 +133,10 @@ bool kvm__arch_load_kernel_image(struct kvm *kvm, int fd_kernel, int fd_initrd, */ pos = limit; pos -= (FDT_MAX_SIZE + FDT_ALIGN); - guest_addr = ALIGN(host_to_guest_flat(kvm, pos), FDT_ALIGN); + guest_addr = host_to_guest_flat(kvm, pos); + if (!guest_addr) + die("fdt too big to contain in guest memory"); + guest_addr = ALIGN(guest_addr, FDT_ALIGN); pos = guest_flat_to_host(kvm, guest_addr); if (pos < kernel_end) die("fdt overlaps with kernel image."); @@ -151,7 +156,10 @@ bool kvm__arch_load_kernel_image(struct kvm *kvm, int fd_kernel, int fd_initrd, die_perror("fstat"); pos -= (sb.st_size + INITRD_ALIGN); - guest_addr = ALIGN(host_to_guest_flat(kvm, pos), INITRD_ALIGN); + guest_addr = host_to_guest_flat(kvm, pos); + if (!guest_addr) + die("initrd too big to fit in the payload memory region"); + guest_addr = ALIGN(guest_addr, INITRD_ALIGN); pos = guest_flat_to_host(kvm, guest_addr); if (pos < kernel_end) die("initrd overlaps with kernel image."); -- 2.47.0