[PATCH v5 3/3] LoongArch: Avoid initrd overlap during kernel relocation

From: WANG Rui

Date: Mon May 11 2026 - 06:52:12 EST


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@xxxxxxxxxx>
Co-authored-by: Huacai Chen <chenhuacai@xxxxxxxxxxx>
Signed-off-by: WANG Rui <r@xxxxxx>
Signed-off-by: Huacai Chen <chenhuacai@xxxxxxxxxxx>
---
arch/loongarch/kernel/relocate.c | 38 ++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)

diff --git a/arch/loongarch/kernel/relocate.c b/arch/loongarch/kernel/relocate.c
index 0a045964fad5..4b61a9632a98 100644
--- a/arch/loongarch/kernel/relocate.c
+++ b/arch/loongarch/kernel/relocate.c
@@ -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
--
2.54.0