Skip to contentSkip to conversation

x86, boot: skip relocs when load address unchanged

PATCH In review Kees Cook 6 participants 7 messages Jan 16, 2015 – Feb 26, 2015 1 message not in the archive x86

lore
Share & tools
lore

Kees Cook

author
#0 lore raw

PATCH 1/1 x86, boot: skip relocs when load address unchanged

On 64-bit, relocation is not required unless the load address gets changed. Without this, relocations do unexpected things when the kernel is above 4G.

---
This is a reimplementation of Baoquan's "kaslr: check if kernel location is changed", which performs the check without needing to change the function declaration. This should have exactly the same effect, but I dropped Vivek's Ack and Thomas's Test, since it's technically a different patch.

---
 arch/x86/boot/compressed/misc.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
arch/x86/boot/compressed/misc.c +8 -1
index dcc1c536cc21..a950864a64da 100644
@@ -373,6 +373,8 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap,
 				  unsigned long output_len,
 				  unsigned long run_size)
 {
+	unsigned char *output_orig = output;
+
 	real_mode = rmode;
 
 	sanitize_boot_params(real_mode);
@@ -421,7 +423,12 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap,
 	debug_putstr("\nDecompressing Linux... ");
 	decompress(input_data, input_len, NULL, NULL, output, NULL, error);
 	parse_elf(output);
-	handle_relocations(output, output_len);
+	/*
+	 * 32-bit always performs relocations. 64-bit relocations are only
+	 * needed if kASLR has chosen a different load address.
+	 */
+	if (!IS_ENABLED(CONFIG_X86_64) || output != output_orig)
+		handle_relocations(output, output_len);
 	debug_putstr("done.\nBooting the kernel.\n");
 	return output;
 }
Signature · 6 lines
1.9.1


-- 
Kees Cook
Chrome OS Security
#1 lore raw

Hi,

Quoted 4 lines · Kees Cook

This is a reimplementation of Baoquan's "kaslr: check if kernel location is changed", which performs the check without needing to change the function declaration. This should have exactly the same effect, but I dropped Vivek's Ack and Thomas's Test, since it's technically a different patch.

Your patch works for me, too.

Tested with linux-3.19-rc4 on a 64-bit system.

Thanks!

-Thomas

#2 lore raw

[tip:x86/urgent] x86, boot: Skip relocs when load address unchanged

In reply to Kees Cook
x86, boot: Skip relocs when load address unchanged
Commit-ID:  f285f4a21c3253887caceed493089ece17579d59
Gitweb:     http://git.kernel.org/tip/f285f4a21c3253887caceed493089ece17579d59
Author:     Kees Cook <[email protected]>
AuthorDate: Thu, 15 Jan 2015 16:51:46 -0800
Committer:  Thomas Gleixner <[email protected]>
CommitDate: Tue, 20 Jan 2015 12:37:23 +0100

x86, boot: Skip relocs when load address unchanged

On 64-bit, relocation is not required unless the load address gets changed. Without this, relocations do unexpected things when the kernel is above 4G.

---
 arch/x86/boot/compressed/misc.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
arch/x86/boot/compressed/misc.c +8 -1
index dcc1c53..a950864 100644
@@ -373,6 +373,8 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap,
 				  unsigned long output_len,
 				  unsigned long run_size)
 {
+	unsigned char *output_orig = output;
+
 	real_mode = rmode;
 
 	sanitize_boot_params(real_mode);
@@ -421,7 +423,12 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap,
 	debug_putstr("\nDecompressing Linux... ");
 	decompress(input_data, input_len, NULL, NULL, output, NULL, error);
 	parse_elf(output);
-	handle_relocations(output, output_len);
+	/*
+	 * 32-bit always performs relocations. 64-bit relocations are only
+	 * needed if kASLR has chosen a different load address.
+	 */
+	if (!IS_ENABLED(CONFIG_X86_64) || output != output_orig)
+		handle_relocations(output, output_len);
 	debug_putstr("done.\nBooting the kernel.\n");
 	return output;
 }
#3 lore raw
In reply to Kees Cook
Quoted 3 lines · Kees Cook

On 64-bit, relocation is not required unless the load address gets changed. Without this, relocations do unexpected things when the kernel is above 4G.

This patch works for me. And good to see it's being merged. About the patch log, I would say relocations do unexpected things when the kernel is above 1G since randomization is done from 16M to 1G, namely CONFIG_RANDOMIZE_BASE_MAX_OFFSET. So above 1G kernel text mapping will step into kernel module mapping region.

BTW, I am working on separate randomization of kernel physical and virtual address , will post it. But it won't conflict with this because I don't think it can be accepted in a short time. Before that this patch truly fix the kexec/kdump bug when kaslr is compiled in.

Thanks
Baoquan

Quoted 47 lines

---
This is a reimplementation of Baoquan's "kaslr: check if kernel location is changed", which performs the check without needing to change the function declaration. This should have exactly the same effect, but I dropped Vivek's Ack and Thomas's Test, since it's technically a different patch.

---
 arch/x86/boot/compressed/misc.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
arch/x86/boot/compressed/misc.c +8 -1
index dcc1c536cc21..a950864a64da 100644
@@ -373,6 +373,8 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap,
 				  unsigned long output_len,
 				  unsigned long run_size)
 {
+	unsigned char *output_orig = output;
+
 	real_mode = rmode;
 
 	sanitize_boot_params(real_mode);
@@ -421,7 +423,12 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap,
 	debug_putstr("\nDecompressing Linux... ");
 	decompress(input_data, input_len, NULL, NULL, output, NULL, error);
 	parse_elf(output);
-	handle_relocations(output, output_len);
+	/*
+	 * 32-bit always performs relocations. 64-bit relocations are only
+	 * needed if kASLR has chosen a different load address.
+	 */
+	if (!IS_ENABLED(CONFIG_X86_64) || output != output_orig)
+		handle_relocations(output, output_len);
 	debug_putstr("done.\nBooting the kernel.\n");
 	return output;
 }
Signature · 6 lines
1.9.1


-- 
Kees Cook
Chrome OS Security
#4 lore raw

Thanks for this patch, and good to see it in mainline!

This actually fixes the problem I reported here:
https://lkml.org/lkml/2014/12/1/15

I wish it to be backported into the Ubuntu Utopic kernel asap.

Quoted 5 lines

This patch works for me. And good to see it's being merged. About the patch log, I would say relocations do unexpected things when the kernel is above 1G since randomization is done from 16M to 1G, namely CONFIG_RANDOMIZE_BASE_MAX_OFFSET. So above 1G kernel text mapping will step into kernel module mapping region.

I'm just speculating, but is it the reason why I only get problems when I boot with kexec? Maybe it's only then when the kernel gets above 1G. Otherwise, the kernels boot properly when they are booted from GRUB.

2015-01-20 14:04 GMT+01:00 Baoquan He <[email protected]>:

Quoted 72 lines · Kees Cook
Quoted 3 lines

On 64-bit, relocation is not required unless the load address gets changed. Without this, relocations do unexpected things when the kernel is above 4G.

This patch works for me. And good to see it's being merged. About the patch log, I would say relocations do unexpected things when the kernel is above 1G since randomization is done from 16M to 1G, namely CONFIG_RANDOMIZE_BASE_MAX_OFFSET. So above 1G kernel text mapping will step into kernel module mapping region.

BTW, I am working on separate randomization of kernel physical and virtual address , will post it. But it won't conflict with this because I don't think it can be accepted in a short time. Before that this patch truly fix the kexec/kdump bug when kaslr is compiled in.

Thanks
Baoquan

Quoted 44 lines

---
This is a reimplementation of Baoquan's "kaslr: check if kernel location is changed", which performs the check without needing to change the function declaration. This should have exactly the same effect, but I dropped Vivek's Ack and Thomas's Test, since it's technically a different patch.

---
 arch/x86/boot/compressed/misc.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
arch/x86/boot/compressed/misc.c +8 -2
index dcc1c536cc21..a950864a64da 100644
@@ -373,6 +373,8 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap,
                                unsigned long output_len,
                                unsigned long run_size)
 {
+     unsigned char *output_orig = output;
+
      real_mode = rmode;
 
      sanitize_boot_params(real_mode);
@@ -421,7 +423,12 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap,
      debug_putstr("\nDecompressing Linux... ");
      decompress(input_data, input_len, NULL, NULL, output, NULL, error);
      parse_elf(output);
-     handle_relocations(output, output_len);
+     /*
+      * 32-bit always performs relocations. 64-bit relocations are only
+      * needed if kASLR has chosen a different load address.
+      */
+     if (!IS_ENABLED(CONFIG_X86_64) || output != output_orig)
+             handle_relocations(output, output_len);
      debug_putstr("done.\nBooting the kernel.\n");
      return output;
 }
--

1.9.1

Quoted 3 lines

--
Kees Cook
Chrome OS Security

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#5 lore raw
Quoted 13 lines · MegaBrutal

Thanks for this patch, and good to see it in mainline!

This actually fixes the problem I reported here:
https://lkml.org/lkml/2014/12/1/15

I wish it to be backported into the Ubuntu Utopic kernel asap.

Quoted 5 lines

This patch works for me. And good to see it's being merged. About the patch log, I would say relocations do unexpected things when the kernel is above 1G since randomization is done from 16M to 1G, namely CONFIG_RANDOMIZE_BASE_MAX_OFFSET. So above 1G kernel text mapping will step into kernel module mapping region.

Quoted 4 lines

I'm just speculating, but is it the reason why I only get problems when I boot with kexec? Maybe it's only then when the kernel gets above 1G. Otherwise, the kernels boot properly when they are booted from GRUB.

Yeah, kexec loads kernel at the top of physical memory. And above 1G kernel mapping will step into module mapping area and collapse system. Grub doesn't incur this problem.

A message that is not in the archive (sent off-list or not yet received): CAE8gLh=9S-FMcekB43nM6RaAxEHsPkU0PK_=wNNbMT9YzkZiXw@mail.gmail.com

#7 lore raw
Quoted 6 lines · MegaBrutal

Thanks for this patch, and good to see it in mainline!

This actually fixes the problem I reported here:
https://lkml.org/lkml/2014/12/1/15

I wish it to be backported into the Ubuntu Utopic kernel asap.

We marked the commit Cc: stable, so it ought to be picked up by the stable kernel and then it should show up in Ubuntu if they refresh their kernel package to the latest stable kernel.

Thanks,

Ingo