Hi,

On amd64 EFI machines, the kernel is potentially booted with an 
obsolete memory map.

The file is "src/sys/arch/amd64/stand/libsa/exec_i386.c"

The memory map is passed to the kernel:

        /* Pass memory map to the kernel */
        mem_pass();

and then later, efi_cleanup() is called:

        /*
         * Move the loaded kernel image to the usual place after calling
         * ExitBootServices().
         */
        delta = DEFAULT_KERNEL_ADDRESS - efi_loadaddr;
        efi_cleanup();


For reference, here is efi_cleanup() from 
"src/sys/arch/amd64/stand/efiboot/efiboot.c":

void
efi_cleanup(void)
{
        int              retry;
        EFI_STATUS       status;

        /* retry once in case of failure */
        for (retry = 1; retry >= 0; retry--) {
                efi_memprobe_internal();        /* sync the current map */
                status = EFI_CALL(BS->ExitBootServices, IH, mmap_key);
                if (status == EFI_SUCCESS)
                        break;
                if (retry == 0)
                        panic("ExitBootServices failed (%d)", status);
        }
}


When ExitBootServices is invoked, it has the potential to change 
the memory map. If this happens, it must be invoked a 2nd time.
This is normal behaviour according to the EFI specification, so 
there is nothing wrong with efi_cleanup() itself.

The bug is that the potentially updated memory map is never 
passed to the kernel in "exec_i386.c".

My understanding is that if the kernel boots with an obsolete 
memory map, it would then have the potential to corrupt EFI 
data in memory, with unpredictable and possibly catastrophic 
results.

The solution would be to ensure that the memory map is passed 
to the kernel *after* efi_cleanup() in "exec_i386.c".

This diff is my attempt to resolve the problem (and clean up 
some redundancy in the ifdef branching). I'd like to submit it here 
for review. I'm running 6.2 -stable, but this diff is against the most 
current version of the file at the time of writing.


diff -ur a/sys/arch/amd64/stand/libsa/exec_i386.c 
b/sys/arch/amd64/stand/libsa/exec_i386.c
--- a/sys/arch/amd64/stand/libsa/exec_i386.c    Sun Feb 11 22:37:16 2018
+++ b/sys/arch/amd64/stand/libsa/exec_i386.c    Sun Feb 11 22:40:23 2018
@@ -78,7 +78,7 @@
        bios_bootsr_t bootsr;
        struct sr_boot_volume *bv;
 #endif
-#if defined(EFIBOOT)
+#ifdef EFIBOOT
        int i;
        u_long delta;
        extern u_long efi_loadaddr;
@@ -86,6 +86,7 @@
        if ((av = alloc(ac)) == NULL)
                panic("alloc for bootarg");
        efi_makebootargs();
+       delta = DEFAULT_KERNEL_ADDRESS - efi_loadaddr;
 #endif
        if (sa_cleanup != NULL)
                (*sa_cleanup)();
@@ -124,6 +125,17 @@
        sr_clear_keys();
 #endif
 
+       entry = marks[MARK_ENTRY] & 0x0fffffff;
+#ifdef EFIBOOT
+       entry += delta;
+#endif
+
+       printf("entry point at 0x%lx\n", entry);
+
+#ifdef EFIBOOT
+       /* Sync the memory map and call ExitBootServices() */
+       efi_cleanup();
+#endif
        /* Pass memory map to the kernel */
        mem_pass();
 
@@ -137,33 +149,24 @@
        makebootargs(av, &ac);
 #endif
 
-       entry = marks[MARK_ENTRY] & 0x0fffffff;
-
-       printf("entry point at 0x%lx\n", entry);
-
-#ifndef EFIBOOT
-       /* stack and the gung is ok at this point, so, no need for asm setup */
-       (*(startfuncp)entry)(howto, bootdev, BOOTARG_APIVER, marks[MARK_END],
-           extmem, cnvmem, ac, (int)av);
-#else
+#ifdef EFIBOOT
        /*
         * Move the loaded kernel image to the usual place after calling
         * ExitBootServices().
         */
-       delta = DEFAULT_KERNEL_ADDRESS - efi_loadaddr;
-       efi_cleanup();
        memcpy((void *)marks[MARK_START] + delta, (void *)marks[MARK_START],
            marks[MARK_END] - marks[MARK_START]);
        for (i = 0; i < MARK_MAX; i++)
                marks[i] += delta;
-       entry += delta;
+#endif
+
 #ifdef __amd64__
        (*run_i386)((u_long)run_i386, entry, howto, bootdev, BOOTARG_APIVER,
            marks[MARK_END], extmem, cnvmem, ac, (intptr_t)av);
 #else
+       /* stack and the gung is ok at this point, so, no need for asm setup */
        (*(startfuncp)entry)(howto, bootdev, BOOTARG_APIVER, marks[MARK_END],
            extmem, cnvmem, ac, (int)av);
-#endif
 #endif
        /* not reached */
 }

Reply via email to