f0ad2aaa26
Resolves: CVE-2022-28736 CVE-2022-28735 CVE-2022-28734 CVE-2022-28733 Resolves: CVE-2021-3697 CVE-2021-3696 CVE-2021-3695 Signed-off-by: Robbie Harwood <rharwood@redhat.com>
379 lines
12 KiB
Diff
379 lines
12 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Peter Jones <pjones@redhat.com>
|
|
Date: Mon, 21 Mar 2022 17:45:40 -0400
|
|
Subject: [PATCH] modules: load module sections at page-aligned addresses
|
|
|
|
Currently we load module sections at whatever alignment gcc+ld happened
|
|
to dump into the ELF section header, which is often pretty useless. For
|
|
example, by default time.mod has these sections on a current x86_64
|
|
build:
|
|
|
|
$ eu-readelf -a grub-core/time.mod |& grep ^Section -A13
|
|
Section Headers:
|
|
[Nr] Name Type Addr Off Size ES Flags Lk Inf Al
|
|
[ 0] NULL 0 00000000 00000000 0 0 0 0
|
|
[ 1] .text PROGBITS 0 00000040 0000015e 0 AX 0 0 1
|
|
[ 2] .rela.text RELA 0 00000458 000001e0 24 I 8 1 8
|
|
[ 3] .rodata.str1.1 PROGBITS 0 0000019e 000000a1 1 AMS 0 0 1
|
|
[ 4] .module_license PROGBITS 0 00000240 0000000f 0 A 0 0 8
|
|
[ 5] .data PROGBITS 0 0000024f 00000000 0 WA 0 0 1
|
|
[ 6] .bss NOBITS 0 00000250 00000008 0 WA 0 0 8
|
|
[ 7] .modname PROGBITS 0 00000250 00000005 0 0 0 1
|
|
[ 8] .symtab SYMTAB 0 00000258 00000150 24 9 6 8
|
|
[ 9] .strtab STRTAB 0 000003a8 000000ab 0 0 0 1
|
|
[10] .shstrtab STRTAB 0 00000638 00000059 0 0 0 1
|
|
|
|
With NX protections being page based, loading sections with either a 1
|
|
or 8 *byte* alignment does absolutely nothing to help us out.
|
|
|
|
This patch switches most EFI platforms to load module sections at 4kB
|
|
page-aligned addresses. To do so, it adds an new per-arch function,
|
|
grub_arch_dl_min_alignment(), which returns the alignment needed for
|
|
dynamically loaded sections (in bytes). Currently it sets it to 4096
|
|
when GRUB_MACHINE_EFI is true on x86_64, i386, arm, arm64, and emu, and
|
|
1-byte alignment on everything else.
|
|
|
|
It then changes the allocation size computation and the loader code in
|
|
grub_dl_load_segments() to align the locations and sizes up to these
|
|
boundaries, and fills any added padding with zeros.
|
|
|
|
All of this happens before relocations are applied, so the relocations
|
|
factor that in with no change.
|
|
|
|
As an aside, initially Daniel Kiper and I thought that it might be a
|
|
better idea to split the modules up into top-level sections as
|
|
.text.modules, .rodata.modules, .data.modules, etc., so that their page
|
|
permissions would get set by the loader that's loading grub itself.
|
|
This turns out to have two significant downsides: 1) either in mkimage
|
|
or in grub_dl_relocate_symbols(), you wind up having to dynamically
|
|
process the relocations to accommodate the moved module sections, and 2)
|
|
you then need to change the permissions on the modules and change them
|
|
back while relocating them in grub_dl_relocate_symbols(), which means
|
|
that any loader that /does/ honor the section flags but does /not/
|
|
generally support NX with the memory attributes API will cause grub to
|
|
fail.
|
|
|
|
Signed-off-by: Peter Jones <pjones@redhat.com>
|
|
---
|
|
grub-core/kern/arm/dl.c | 13 +++++++++++++
|
|
grub-core/kern/arm64/dl.c | 13 +++++++++++++
|
|
grub-core/kern/dl.c | 29 +++++++++++++++++++++--------
|
|
grub-core/kern/emu/full.c | 13 +++++++++++++
|
|
grub-core/kern/i386/dl.c | 13 +++++++++++++
|
|
grub-core/kern/ia64/dl.c | 9 +++++++++
|
|
grub-core/kern/mips/dl.c | 8 ++++++++
|
|
grub-core/kern/powerpc/dl.c | 9 +++++++++
|
|
grub-core/kern/riscv/dl.c | 13 +++++++++++++
|
|
grub-core/kern/sparc64/dl.c | 9 +++++++++
|
|
grub-core/kern/x86_64/dl.c | 13 +++++++++++++
|
|
include/grub/dl.h | 2 ++
|
|
docs/grub-dev.texi | 6 +++---
|
|
13 files changed, 139 insertions(+), 11 deletions(-)
|
|
|
|
diff --git a/grub-core/kern/arm/dl.c b/grub-core/kern/arm/dl.c
|
|
index eab9d17ff2..9260737936 100644
|
|
--- a/grub-core/kern/arm/dl.c
|
|
+++ b/grub-core/kern/arm/dl.c
|
|
@@ -278,3 +278,16 @@ grub_arch_dl_check_header (void *ehdr)
|
|
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
+
|
|
+/*
|
|
+ * Tell the loader what our minimum section alignment is.
|
|
+ */
|
|
+grub_size_t
|
|
+grub_arch_dl_min_alignment (void)
|
|
+{
|
|
+#ifdef GRUB_MACHINE_EFI
|
|
+ return 4096;
|
|
+#else
|
|
+ return 1;
|
|
+#endif
|
|
+}
|
|
diff --git a/grub-core/kern/arm64/dl.c b/grub-core/kern/arm64/dl.c
|
|
index 512e5a80b0..0d4a26857f 100644
|
|
--- a/grub-core/kern/arm64/dl.c
|
|
+++ b/grub-core/kern/arm64/dl.c
|
|
@@ -196,3 +196,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
|
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
+
|
|
+/*
|
|
+ * Tell the loader what our minimum section alignment is.
|
|
+ */
|
|
+grub_size_t
|
|
+grub_arch_dl_min_alignment (void)
|
|
+{
|
|
+#ifdef GRUB_MACHINE_EFI
|
|
+ return 4096;
|
|
+#else
|
|
+ return 1;
|
|
+#endif
|
|
+}
|
|
diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
|
|
index aef8af8aa7..8c7aacef39 100644
|
|
--- a/grub-core/kern/dl.c
|
|
+++ b/grub-core/kern/dl.c
|
|
@@ -277,7 +277,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
|
|
{
|
|
unsigned i;
|
|
const Elf_Shdr *s;
|
|
- grub_size_t tsize = 0, talign = 1;
|
|
+ grub_size_t tsize = 0, talign = 1, arch_addralign = 1;
|
|
#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
|
|
grub_size_t tramp;
|
|
grub_size_t got;
|
|
@@ -285,16 +285,24 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
|
|
#endif
|
|
char *ptr;
|
|
|
|
+ arch_addralign = grub_arch_dl_min_alignment ();
|
|
+
|
|
for (i = 0, s = (const Elf_Shdr *)((const char *) e + e->e_shoff);
|
|
i < e->e_shnum;
|
|
i++, s = (const Elf_Shdr *)((const char *) s + e->e_shentsize))
|
|
{
|
|
+ grub_size_t sh_addralign;
|
|
+ grub_size_t sh_size;
|
|
+
|
|
if (s->sh_size == 0 || !(s->sh_flags & SHF_ALLOC))
|
|
continue;
|
|
|
|
- tsize = ALIGN_UP (tsize, s->sh_addralign) + s->sh_size;
|
|
- if (talign < s->sh_addralign)
|
|
- talign = s->sh_addralign;
|
|
+ sh_addralign = ALIGN_UP(s->sh_addralign, arch_addralign);
|
|
+ sh_size = ALIGN_UP(s->sh_size, sh_addralign);
|
|
+
|
|
+ tsize = ALIGN_UP (tsize, sh_addralign) + sh_size;
|
|
+ if (talign < sh_addralign)
|
|
+ talign = sh_addralign;
|
|
}
|
|
|
|
#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
|
|
@@ -323,6 +331,9 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
|
|
i < e->e_shnum;
|
|
i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
|
|
{
|
|
+ grub_size_t sh_addralign = ALIGN_UP(s->sh_addralign, arch_addralign);
|
|
+ grub_size_t sh_size = ALIGN_UP(s->sh_size, sh_addralign);
|
|
+
|
|
if (s->sh_flags & SHF_ALLOC)
|
|
{
|
|
grub_dl_segment_t seg;
|
|
@@ -335,17 +346,19 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
|
|
{
|
|
void *addr;
|
|
|
|
- ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, s->sh_addralign);
|
|
+ ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, sh_addralign);
|
|
addr = ptr;
|
|
- ptr += s->sh_size;
|
|
+ ptr += sh_size;
|
|
|
|
switch (s->sh_type)
|
|
{
|
|
case SHT_PROGBITS:
|
|
grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size);
|
|
+ grub_memset ((char *)addr + s->sh_size, 0,
|
|
+ sh_size - s->sh_size);
|
|
break;
|
|
case SHT_NOBITS:
|
|
- grub_memset (addr, 0, s->sh_size);
|
|
+ grub_memset (addr, 0, sh_size);
|
|
break;
|
|
}
|
|
|
|
@@ -354,7 +367,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
|
|
else
|
|
seg->addr = 0;
|
|
|
|
- seg->size = s->sh_size;
|
|
+ seg->size = sh_size;
|
|
seg->section = i;
|
|
seg->next = mod->segment;
|
|
mod->segment = seg;
|
|
diff --git a/grub-core/kern/emu/full.c b/grub-core/kern/emu/full.c
|
|
index e8d63b1f5f..1de1c28eb0 100644
|
|
--- a/grub-core/kern/emu/full.c
|
|
+++ b/grub-core/kern/emu/full.c
|
|
@@ -67,3 +67,16 @@ grub_arch_dl_init_linker (void)
|
|
}
|
|
#endif
|
|
|
|
+
|
|
+/*
|
|
+ * Tell the loader what our minimum section alignment is.
|
|
+ */
|
|
+grub_size_t
|
|
+grub_arch_dl_min_alignment (void)
|
|
+{
|
|
+#ifdef GRUB_MACHINE_EFI
|
|
+ return 4096;
|
|
+#else
|
|
+ return 1;
|
|
+#endif
|
|
+}
|
|
diff --git a/grub-core/kern/i386/dl.c b/grub-core/kern/i386/dl.c
|
|
index 1346da5cc9..d6b4681fc9 100644
|
|
--- a/grub-core/kern/i386/dl.c
|
|
+++ b/grub-core/kern/i386/dl.c
|
|
@@ -79,3 +79,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
|
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
+
|
|
+/*
|
|
+ * Tell the loader what our minimum section alignment is.
|
|
+ */
|
|
+grub_size_t
|
|
+grub_arch_dl_min_alignment (void)
|
|
+{
|
|
+#ifdef GRUB_MACHINE_EFI
|
|
+ return 4096;
|
|
+#else
|
|
+ return 1;
|
|
+#endif
|
|
+}
|
|
diff --git a/grub-core/kern/ia64/dl.c b/grub-core/kern/ia64/dl.c
|
|
index db59300fea..92d82c5750 100644
|
|
--- a/grub-core/kern/ia64/dl.c
|
|
+++ b/grub-core/kern/ia64/dl.c
|
|
@@ -148,3 +148,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
|
}
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
+
|
|
+/*
|
|
+ * Tell the loader what our minimum section alignment is.
|
|
+ */
|
|
+grub_size_t
|
|
+grub_arch_dl_min_alignment (void)
|
|
+{
|
|
+ return 1;
|
|
+}
|
|
diff --git a/grub-core/kern/mips/dl.c b/grub-core/kern/mips/dl.c
|
|
index 5d7d299c74..6d83bd71e9 100644
|
|
--- a/grub-core/kern/mips/dl.c
|
|
+++ b/grub-core/kern/mips/dl.c
|
|
@@ -272,3 +272,11 @@ grub_arch_dl_init_linker (void)
|
|
grub_dl_register_symbol ("_gp_disp", &_gp_disp_dummy, 0, 0);
|
|
}
|
|
|
|
+/*
|
|
+ * Tell the loader what our minimum section alignment is.
|
|
+ */
|
|
+grub_size_t
|
|
+grub_arch_dl_min_alignment (void)
|
|
+{
|
|
+ return 1;
|
|
+}
|
|
diff --git a/grub-core/kern/powerpc/dl.c b/grub-core/kern/powerpc/dl.c
|
|
index cdd61b305f..5d9ba2e158 100644
|
|
--- a/grub-core/kern/powerpc/dl.c
|
|
+++ b/grub-core/kern/powerpc/dl.c
|
|
@@ -167,3 +167,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
|
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
+
|
|
+/*
|
|
+ * Tell the loader what our minimum section alignment is.
|
|
+ */
|
|
+grub_size_t
|
|
+grub_arch_dl_min_alignment (void)
|
|
+{
|
|
+ return 1;
|
|
+}
|
|
diff --git a/grub-core/kern/riscv/dl.c b/grub-core/kern/riscv/dl.c
|
|
index f26b12aaa4..aa18f9e990 100644
|
|
--- a/grub-core/kern/riscv/dl.c
|
|
+++ b/grub-core/kern/riscv/dl.c
|
|
@@ -343,3 +343,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
|
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
+
|
|
+/*
|
|
+ * Tell the loader what our minimum section alignment is.
|
|
+ */
|
|
+grub_size_t
|
|
+grub_arch_dl_min_alignment (void)
|
|
+{
|
|
+#ifdef GRUB_MACHINE_EFI
|
|
+ return 4096;
|
|
+#else
|
|
+ return 1;
|
|
+#endif
|
|
+}
|
|
diff --git a/grub-core/kern/sparc64/dl.c b/grub-core/kern/sparc64/dl.c
|
|
index f3d960186b..f054f08241 100644
|
|
--- a/grub-core/kern/sparc64/dl.c
|
|
+++ b/grub-core/kern/sparc64/dl.c
|
|
@@ -189,3 +189,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
|
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
+
|
|
+/*
|
|
+ * Tell the loader what our minimum section alignment is.
|
|
+ */
|
|
+grub_size_t
|
|
+grub_arch_dl_min_alignment (void)
|
|
+{
|
|
+ return 1;
|
|
+}
|
|
diff --git a/grub-core/kern/x86_64/dl.c b/grub-core/kern/x86_64/dl.c
|
|
index e5a8bdcf4f..a105dc50ce 100644
|
|
--- a/grub-core/kern/x86_64/dl.c
|
|
+++ b/grub-core/kern/x86_64/dl.c
|
|
@@ -119,3 +119,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
|
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
+
|
|
+/*
|
|
+ * Tell the loader what our minimum section alignment is.
|
|
+ */
|
|
+grub_size_t
|
|
+grub_arch_dl_min_alignment (void)
|
|
+{
|
|
+#ifdef GRUB_MACHINE_EFI
|
|
+ return 4096;
|
|
+#else
|
|
+ return 1;
|
|
+#endif
|
|
+}
|
|
diff --git a/include/grub/dl.h b/include/grub/dl.h
|
|
index 618ae6f474..f36ed5cb17 100644
|
|
--- a/include/grub/dl.h
|
|
+++ b/include/grub/dl.h
|
|
@@ -280,6 +280,8 @@ grub_err_t grub_arch_dl_check_header (void *ehdr);
|
|
grub_err_t
|
|
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
|
Elf_Shdr *s, grub_dl_segment_t seg);
|
|
+grub_size_t
|
|
+grub_arch_dl_min_alignment (void);
|
|
#endif
|
|
|
|
#if defined (_mips)
|
|
diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
|
|
index 90083772c8..c23ba313dc 100644
|
|
--- a/docs/grub-dev.texi
|
|
+++ b/docs/grub-dev.texi
|
|
@@ -755,9 +755,9 @@ declare startup asm file ($cpu_$platform_startup) as well as any other files
|
|
(e.g. init.c and callwrap.S) (e.g. $cpu_$platform = kern/$cpu/$platform/init.c).
|
|
At this stage you will also need to add dummy dl.c and cache.S with functions
|
|
grub_err_t grub_arch_dl_check_header (void *ehdr), grub_err_t
|
|
-grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) (dl.c) and
|
|
-void grub_arch_sync_caches (void *address, grub_size_t len) (cache.S). They
|
|
-won't be used for now.
|
|
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) (dl.c), grub_uint32_t
|
|
+grub_arch_dl_min_alignment (void), and void grub_arch_sync_caches (void
|
|
+*address, grub_size_t len) (cache.S). They won't be used for now.
|
|
|
|
You will need to create directory include/$cpu/$platform and a file
|
|
include/$cpu/types.h. The later folowing this template:
|