[RFC PATCH] x86/boot/compressed: Disable physical KASLR when memmap= appears

Ard Biesheuvel posted 1 patch 1 month, 1 week ago
arch/x86/boot/compressed/kaslr.c | 100 ++-----------------------------
1 file changed, 6 insertions(+), 94 deletions(-)
[RFC PATCH] x86/boot/compressed: Disable physical KASLR when memmap= appears
Posted by Ard Biesheuvel 1 month, 1 week ago
From: Ard Biesheuvel <ardb@kernel.org>

The decompressor physical KASLR randomization logic is rather intricate,
as it attempts to reconcile various kernel command line parameters such
as mem=, memmap= and hugepages=, in order to create a coherent picture
of which parts of physical RAM may be used to load the kernel.

The memmap= command line option is especially interesting, as it can
appear an unlimited number of times, and each occurrence can describe a
number of memory regions in a number of different ways (see below), with
no guarantees that those occurrences are mutually consistent.

Given that the current logic appears to be flawed, let's not bother and
just disable physical KASLR when memmap= appears at all.

  memmap=exactmap [KNL,X86,EARLY] Enable setting of an exact
  		E820 memory map, as specified by the user.
  		Such memmap=exactmap lines can be constructed based on
  		BIOS output or other requirements. See the memmap=nn@ss
  		option description.

  memmap=nn[KMG]@ss[KMG]
  		[KNL, X86,MIPS,XTENSA,EARLY] Force usage of a specific region of memory.
  		Region of memory to be used is from ss to ss+nn.
  		If @ss[KMG] is omitted, it is equivalent to mem=nn[KMG],
  		which limits max address to nn[KMG].
  		Multiple different regions can be specified,
  		comma delimited.
  		Example:
  			memmap=100M@2G,100M#3G,1G!1024G

  memmap=nn[KMG]#ss[KMG]
  		[KNL,ACPI,EARLY] Mark specific memory as ACPI data.
  		Region of memory to be marked is from ss to ss+nn.

  memmap=nn[KMG]$ss[KMG]
  		[KNL,ACPI,EARLY] Mark specific memory as reserved.
  		Region of memory to be reserved is from ss to ss+nn.
  		Example: Exclude memory from 0x18690000-0x1869ffff
  			 memmap=64K$0x18690000
  			 or
  			 memmap=0x10000$0x18690000
  		Some bootloaders may need an escape character before '$',
  		like Grub2, otherwise '$' and the following number
  		will be eaten.

  memmap=nn[KMG]!ss[KMG,EARLY]
  		[KNL,X86] Mark specific memory as protected.
  		Region of memory to be used, from ss to ss+nn.
  		The memory region may be marked as e820 type 12 (0xc)
  		and is NVDIMM or ADR memory.

  memmap=<size>%<offset>-<oldtype>+<newtype>
  		[KNL,ACPI,EARLY] Convert memory within the specified region
  		from <oldtype> to <newtype>. If "-<oldtype>" is left
  		out, the whole region will be marked as <newtype>,
  		even if previously unavailable. If "+<newtype>" is left
  		out, matching memory will be removed. Types are
  		specified as e820 types, e.g., 1 = RAM, 2 = reserved,
  		3 = ACPI, 12 = PRAM.

Reported-by: Michal Clapinski <mclapinski@google.com>
Reported-by: Chris Li <chrisl@kernel.org>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 arch/x86/boot/compressed/kaslr.c | 100 ++-----------------------------
 1 file changed, 6 insertions(+), 94 deletions(-)

diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
index 3b0948ad449f..96805d6c10a4 100644
--- a/arch/x86/boot/compressed/kaslr.c
+++ b/arch/x86/boot/compressed/kaslr.c
@@ -67,16 +67,11 @@ static unsigned long get_boot_seed(void)
 #define KASLR_COMPRESSED_BOOT
 #include "../../lib/kaslr.c"
 
-
-/* Only supporting at most 4 unusable memmap regions with kaslr */
-#define MAX_MEMMAP_REGIONS	4
-
-static bool memmap_too_large;
-
+static bool memmap_found;
 
 /*
  * Store memory limit: MAXMEM on 64-bit and KERNEL_IMAGE_SIZE on 32-bit.
- * It may be reduced by "mem=nn[KMG]" or "memmap=nn[KMG]" command line options.
+ * It may be reduced by "mem=nn[KMG]" command line options.
  */
 static u64 mem_limit;
 
@@ -88,8 +83,6 @@ enum mem_avoid_index {
 	MEM_AVOID_INITRD,
 	MEM_AVOID_CMDLINE,
 	MEM_AVOID_BOOTPARAMS,
-	MEM_AVOID_MEMMAP_BEGIN,
-	MEM_AVOID_MEMMAP_END = MEM_AVOID_MEMMAP_BEGIN + MAX_MEMMAP_REGIONS - 1,
 	MEM_AVOID_MAX,
 };
 
@@ -115,87 +108,6 @@ char *skip_spaces(const char *str)
 #include "../../../../lib/ctype.c"
 #include "../../../../lib/cmdline.c"
 
-static int
-parse_memmap(char *p, u64 *start, u64 *size)
-{
-	char *oldp;
-
-	if (!p)
-		return -EINVAL;
-
-	/* We don't care about this option here */
-	if (!strncmp(p, "exactmap", 8))
-		return -EINVAL;
-
-	oldp = p;
-	*size = memparse(p, &p);
-	if (p == oldp)
-		return -EINVAL;
-
-	switch (*p) {
-	case '#':
-	case '$':
-	case '!':
-		*start = memparse(p + 1, &p);
-		return 0;
-	case '@':
-		/*
-		 * memmap=nn@ss specifies usable region, should
-		 * be skipped
-		 */
-		*size = 0;
-		fallthrough;
-	default:
-		/*
-		 * If w/o offset, only size specified, memmap=nn[KMG] has the
-		 * same behaviour as mem=nn[KMG]. It limits the max address
-		 * system can use. Region above the limit should be avoided.
-		 */
-		*start = 0;
-		return 0;
-	}
-
-	return -EINVAL;
-}
-
-static void mem_avoid_memmap(char *str)
-{
-	static int i;
-
-	if (i >= MAX_MEMMAP_REGIONS)
-		return;
-
-	while (str && (i < MAX_MEMMAP_REGIONS)) {
-		int rc;
-		u64 start, size;
-		char *k = strchr(str, ',');
-
-		if (k)
-			*k++ = 0;
-
-		rc = parse_memmap(str, &start, &size);
-		if (rc < 0)
-			break;
-		str = k;
-
-		if (start == 0) {
-			/* Store the specified memory limit if size > 0 */
-			if (size > 0 && size < mem_limit)
-				mem_limit = size;
-
-			continue;
-		}
-
-		mem_avoid[MEM_AVOID_MEMMAP_BEGIN + i].start = start;
-		mem_avoid[MEM_AVOID_MEMMAP_BEGIN + i].size = size;
-		i++;
-	}
-
-	/* More than 4 memmaps, fail kaslr */
-	if ((i >= MAX_MEMMAP_REGIONS) && str)
-		memmap_too_large = true;
-}
-
 /* Store the number of 1GB huge pages which users specified: */
 static unsigned long max_gb_huge_pages;
 
@@ -254,7 +166,7 @@ static void handle_mem_options(void)
 			break;
 
 		if (!strcmp(param, "memmap")) {
-			mem_avoid_memmap(val);
+			memmap_found = true;
 		} else if (IS_ENABLED(CONFIG_X86_64) && strstr(param, "hugepages")) {
 			parse_gb_huge_pages(param, val);
 		} else if (!strcmp(param, "mem")) {
@@ -812,9 +724,9 @@ static unsigned long find_random_phys_addr(unsigned long minimum,
 	if (minimum + image_size > mem_limit)
 		return 0;
 
-	/* Check if we had too many memmaps. */
-	if (memmap_too_large) {
-		debug_putstr("Aborted memory entries scan (more than 4 memmap= args)!\n");
+	/* Check if memmap= appears on the command line */
+	if (memmap_found) {
+		debug_putstr("memmap= found on the command line, disabling physical KASLR\n");
 		return 0;
 	}
 
-- 
2.51.2.1041.gc1ab5b90ca-goog
Re: [RFC PATCH] x86/boot/compressed: Disable physical KASLR when memmap= appears
Posted by Borislav Petkov 1 month, 1 week ago
On Thu, Nov 06, 2025 at 06:30:20PM +0100, Ard Biesheuvel wrote:
> Reported-by: Michal Clapinski <mclapinski@google.com>
> Reported-by: Chris Li <chrisl@kernel.org>
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
>  arch/x86/boot/compressed/kaslr.c | 100 ++-----------------------------
>  1 file changed, 6 insertions(+), 94 deletions(-)

I love diffstats like that.

How about this simplification ontop of yours:

---

diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
index 96805d6c10a4..7875ae2d514e 100644
--- a/arch/x86/boot/compressed/kaslr.c
+++ b/arch/x86/boot/compressed/kaslr.c
@@ -67,8 +67,6 @@ static unsigned long get_boot_seed(void)
 #define KASLR_COMPRESSED_BOOT
 #include "../../lib/kaslr.c"
 
-static bool memmap_found;
-
 /*
  * Store memory limit: MAXMEM on 64-bit and KERNEL_IMAGE_SIZE on 32-bit.
  * It may be reduced by "mem=nn[KMG]" command line options.
@@ -165,9 +163,7 @@ static void handle_mem_options(void)
 		if (!val && strcmp(param, "--") == 0)
 			break;
 
-		if (!strcmp(param, "memmap")) {
-			memmap_found = true;
-		} else if (IS_ENABLED(CONFIG_X86_64) && strstr(param, "hugepages")) {
+		if (IS_ENABLED(CONFIG_X86_64) && strstr(param, "hugepages")) {
 			parse_gb_huge_pages(param, val);
 		} else if (!strcmp(param, "mem")) {
 			char *p = val;
@@ -724,12 +720,6 @@ static unsigned long find_random_phys_addr(unsigned long minimum,
 	if (minimum + image_size > mem_limit)
 		return 0;
 
-	/* Check if memmap= appears on the command line */
-	if (memmap_found) {
-		debug_putstr("memmap= found on the command line, disabling physical KASLR\n");
-		return 0;
-	}
-
 	/*
 	 * During kexec handover only process KHO scratch areas that are known
 	 * not to contain any data that must be preserved.
@@ -783,6 +773,11 @@ void choose_random_location(unsigned long input,
 		return;
 	}
 
+	if (cmdline_find_option_bool("memmap")) {
+		warn("KASLR disabled: 'memmap' on cmdline.");
+		return;
+	}
+
 	boot_params_ptr->hdr.loadflags |= KASLR_FLAG;
 
 	if (IS_ENABLED(CONFIG_X86_32))


-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
Re: [RFC PATCH] x86/boot/compressed: Disable physical KASLR when memmap= appears
Posted by Ard Biesheuvel 1 month, 1 week ago
On Fri, 7 Nov 2025 at 21:50, Borislav Petkov <bp@alien8.de> wrote:
>
> On Thu, Nov 06, 2025 at 06:30:20PM +0100, Ard Biesheuvel wrote:
> > Reported-by: Michal Clapinski <mclapinski@google.com>
> > Reported-by: Chris Li <chrisl@kernel.org>
> > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> > ---
> >  arch/x86/boot/compressed/kaslr.c | 100 ++-----------------------------
> >  1 file changed, 6 insertions(+), 94 deletions(-)
>
> I love diffstats like that.
>
> How about this simplification ontop of yours:
>

Yes, better, although we should still clarify that only physical
randomization is affected.

> ---
>
> diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
> index 96805d6c10a4..7875ae2d514e 100644
> --- a/arch/x86/boot/compressed/kaslr.c
> +++ b/arch/x86/boot/compressed/kaslr.c
> @@ -67,8 +67,6 @@ static unsigned long get_boot_seed(void)
>  #define KASLR_COMPRESSED_BOOT
>  #include "../../lib/kaslr.c"
>
> -static bool memmap_found;
> -
>  /*
>   * Store memory limit: MAXMEM on 64-bit and KERNEL_IMAGE_SIZE on 32-bit.
>   * It may be reduced by "mem=nn[KMG]" command line options.
> @@ -165,9 +163,7 @@ static void handle_mem_options(void)
>                 if (!val && strcmp(param, "--") == 0)
>                         break;
>
> -               if (!strcmp(param, "memmap")) {
> -                       memmap_found = true;
> -               } else if (IS_ENABLED(CONFIG_X86_64) && strstr(param, "hugepages")) {
> +               if (IS_ENABLED(CONFIG_X86_64) && strstr(param, "hugepages")) {
>                         parse_gb_huge_pages(param, val);
>                 } else if (!strcmp(param, "mem")) {
>                         char *p = val;
> @@ -724,12 +720,6 @@ static unsigned long find_random_phys_addr(unsigned long minimum,
>         if (minimum + image_size > mem_limit)
>                 return 0;
>
> -       /* Check if memmap= appears on the command line */
> -       if (memmap_found) {
> -               debug_putstr("memmap= found on the command line, disabling physical KASLR\n");
> -               return 0;
> -       }
> -
>         /*
>          * During kexec handover only process KHO scratch areas that are known
>          * not to contain any data that must be preserved.
> @@ -783,6 +773,11 @@ void choose_random_location(unsigned long input,
>                 return;
>         }
>
> +       if (cmdline_find_option_bool("memmap")) {
> +               warn("KASLR disabled: 'memmap' on cmdline.");
> +               return;
> +       }
> +
>         boot_params_ptr->hdr.loadflags |= KASLR_FLAG;
>
>         if (IS_ENABLED(CONFIG_X86_32))
>
>
> --
> Regards/Gruss,
>     Boris.
>
> https://people.kernel.org/tglx/notes-about-netiquette
Re: [RFC PATCH] x86/boot/compressed: Disable physical KASLR when memmap= appears
Posted by Borislav Petkov 1 month, 1 week ago
On Tue, Nov 11, 2025 at 05:52:58PM +0100, Ard Biesheuvel wrote:
> Yes, better, although we should still clarify that only physical
> randomization is affected.

Right.

I'm thinking we could take this fix for a spin next cycle to see who would cry
out and why - ala "hey, you broke my super important memmap= use case with
KASLR..." and then see what we'd end up committing...

Thx.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette