]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
LoongArch: Avoid initrd overlap during kernel relocation
authorWANG Rui <wangrui@loongson.cn>
Thu, 21 May 2026 12:58:36 +0000 (20:58 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Thu, 21 May 2026 12:58:36 +0000 (20:58 +0800)
Validate the relocation address against the initrd region specified via
"initrd=" or "initrdmem=" on the command line. Reject relocation targets
that overlap the initrd to prevent memory corruption during early boot.

Acked-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: WANG Rui <wangrui@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
arch/loongarch/kernel/relocate.c

index 0a045964fad5f8ea4fdcfa6178855d2bdf62b07c..4b61a9632a9801f452d7428b4b1eea8b2be824d9 100644 (file)
@@ -222,14 +222,52 @@ static inline void __init *determine_relocation_address(void)
        return RELOCATED_KASLR(destination);
 }
 
+static unsigned long __init determine_initrd_address(unsigned long *size)
+{
+       unsigned long start = 0;
+       unsigned long key_length;
+       char *p, *endp, *key = "initrd=";
+
+       key_length = strlen(key);
+       p = strstr(boot_command_line, key);
+
+       if (!p) {
+               key = "initrdmem=";
+               key_length = strlen(key);
+               p = strstr(boot_command_line, key);
+       }
+
+       if (p == boot_command_line || (p > boot_command_line && *(p - 1) == ' ')) {
+               p += key_length;
+               start = memparse(p, &endp);
+               if (*endp == ',')
+                       *size = memparse(endp + 1, NULL);
+       }
+
+       return start;
+}
+
 static inline int __init relocation_addr_valid(void *location_new)
 {
+       unsigned long kernel_start, kernel_size;
+       unsigned long initrd_start, initrd_size = 0;
+
        if ((unsigned long)location_new & 0x00000ffff)
                return 0; /* Inappropriately aligned new location */
 
        if ((unsigned long)location_new < (unsigned long)_end)
                return 0; /* New location overlaps original kernel */
 
+       initrd_start = determine_initrd_address(&initrd_size);
+       if (initrd_start && initrd_size) {
+               kernel_start = PHYSADDR(location_new);
+               kernel_size = (unsigned long)_end - (unsigned long)_text;
+
+               if (kernel_start < (initrd_start + initrd_size) &&
+                       initrd_start < (kernel_start + kernel_size))
+                       return 0; /* initrd/initramfs overlaps kernel */
+       }
+
        return 1;
 }
 #endif