Runtime relocation entries are not guaranteed to be sorted by target
address. This was observed with GNU ld 2.42 when linking qemu-x86 using
--emit-relocs and -pie: after an unrelated image-layout change, .rel.dyn
contained 450 address decreases. The old code skipped relocation of
mem_malloc_brk in sbrk(), causing allocation to fail and log_init() to
abort boot.

The ordering check was added as a cheap corruption guard because .bss
overlays __rel_dyn_start and may overwrite relocation entries before
relocation. Replace it with lower and upper image-bound checks. Together
with the existing type and target checks, these reject most corrupted
entries without requiring sorted input. A corrupted entry whose location
and target both happen to be in range may still be applied; the ordering
check likewise could not reject corruption that preserved increasing
addresses.

Apply the same correction to the 32-bit and 64-bit paths.

Fixes: b50b1633c022 ("x86: Add support for 64-bit relocation")
Fixes: 62f7970a5a06 ("x86: Add error checking to x86 relocation code")
Signed-off-by: Jerome Forissier <[email protected]>
---
 arch/x86/lib/relocate.c | 24 +++++++++++-------------
 1 file changed, 11 insertions(+), 13 deletions(-)

diff --git a/arch/x86/lib/relocate.c b/arch/x86/lib/relocate.c
index 9ce56062d2..461f6f8a52 100644
--- a/arch/x86/lib/relocate.c
+++ b/arch/x86/lib/relocate.c
@@ -52,7 +52,7 @@ int clear_bss(void)
 static void do_elf_reloc_fixups64(unsigned int text_base, uintptr_t size,
                                  Elf64_Rela *re_src, Elf64_Rela *re_end)
 {
-       Elf64_Addr *offset_ptr_rom, *last_offset = NULL;
+       Elf64_Addr *offset_ptr_rom;
        Elf64_Addr *offset_ptr_ram;
 
        do {
@@ -70,7 +70,7 @@ static void do_elf_reloc_fixups64(unsigned int text_base, 
uintptr_t size,
 
                /* Check that the location of the relocation is in .text */
                if (offset_ptr_rom >= (Elf64_Addr *)(uintptr_t)text_base &&
-                   offset_ptr_rom > last_offset) {
+                   offset_ptr_rom < (Elf64_Addr *)(text_base + size)) {
                        /* Switch to the in-RAM version */
                        offset_ptr_ram = (Elf64_Addr *)((ulong)offset_ptr_rom +
                                                        gd->reloc_off);
@@ -87,11 +87,9 @@ static void do_elf_reloc_fixups64(unsigned int text_base, 
uintptr_t size,
                                      (ulong)*offset_ptr_ram, text_base + size);
                        }
                } else {
-                       debug("   %p: %lx: rom reloc %lx, last %p\n", re_src,
-                             (ulong)re_src->r_info, (ulong)re_src->r_offset,
-                             last_offset);
+                       debug("   %p: %lx: rom reloc %lx outside image\n", 
re_src,
+                             (ulong)re_src->r_info, (ulong)re_src->r_offset);
                }
-               last_offset = offset_ptr_rom;
 
        } while (++re_src < re_end);
 }
@@ -99,7 +97,7 @@ static void do_elf_reloc_fixups64(unsigned int text_base, 
uintptr_t size,
 static void do_elf_reloc_fixups32(unsigned int text_base, uintptr_t size,
                                  Elf32_Rel *re_src, Elf32_Rel *re_end)
 {
-       Elf32_Addr *offset_ptr_rom, *last_offset = NULL;
+       Elf32_Addr *offset_ptr_rom;
        Elf32_Addr *offset_ptr_ram;
 
        do {
@@ -117,7 +115,7 @@ static void do_elf_reloc_fixups32(unsigned int text_base, 
uintptr_t size,
 
                /* Check that the location of the relocation is in .text */
                if (offset_ptr_rom >= (Elf32_Addr *)(uintptr_t)text_base &&
-                   offset_ptr_rom > last_offset) {
+                   offset_ptr_rom < (Elf32_Addr *)(text_base + size)) {
 
                        /* Switch to the in-RAM version */
                        offset_ptr_ram = (Elf32_Addr *)((ulong)offset_ptr_rom +
@@ -133,18 +131,18 @@ static void do_elf_reloc_fixups32(unsigned int text_base, 
uintptr_t size,
                                      *offset_ptr_ram, text_base + size);
                        }
                } else {
-                       debug("   %p: rom reloc %x, last %p\n", re_src,
-                              re_src->r_offset, last_offset);
+                       debug("   %p: rom reloc %x outside image\n", re_src,
+                             re_src->r_offset);
                }
-               last_offset = offset_ptr_rom;
 
        } while (++re_src < re_end);
 }
 #endif
 
 /*
- * This function has more error checking than you might expect. Please see
- * this commit message for more information:
+ * The relocation table can be corrupted by .bss writes before relocation.
+ * Validate types, locations and targets to reject most corrupted entries.
+ * Please see this commit message for more information:
  *    62f7970a x86: Add error checking to x86 relocation code
  */
 int do_elf_reloc_fixups(void)

Reply via email to