From 431a111c60095fc973d83fe9209f26f29ce78784 Mon Sep 17 00:00:00 2001 From: Hitendra Prajapati Date: Mon, 1 Aug 2022 11:17:17 +0530 Subject: [PATCH] CVE-2022-28736 Upstream-Status: Backport [https://git.savannah.gnu.org/gitweb/?p=grub.git;a=commit;h=04c86e0bb7b58fc2f913f798cdb18934933e532d] CVE: CVE-2022-28736 Signed-off-by: Hitendra Prajapati loader/efi/chainloader: Use grub_loader_set_ex() This ports the EFI chainloader to use grub_loader_set_ex() in order to fix a use-after-free bug that occurs when grub_cmd_chainloader() is executed more than once before a boot attempt is performed. Fixes: CVE-2022-28736 Signed-off-by: Chris Coulson Reviewed-by: Daniel Kiper --- grub-core/commands/boot.c | 66 ++++++++++++++++++++++++++---- grub-core/loader/efi/chainloader.c | 46 +++++++++++---------- include/grub/loader.h | 5 +++ 3 files changed, 87 insertions(+), 30 deletions(-) diff --git a/grub-core/commands/boot.c b/grub-core/commands/boot.c index bbca81e..6151478 100644 --- a/grub-core/commands/boot.c +++ b/grub-core/commands/boot.c @@ -27,10 +27,20 @@ GRUB_MOD_LICENSE ("GPLv3+"); -static grub_err_t (*grub_loader_boot_func) (void); -static grub_err_t (*grub_loader_unload_func) (void); +static grub_err_t (*grub_loader_boot_func) (void *context); +static grub_err_t (*grub_loader_unload_func) (void *context); +static void *grub_loader_context; static int grub_loader_flags; +struct grub_simple_loader_hooks +{ + grub_err_t (*boot) (void); + grub_err_t (*unload) (void); +}; + +/* Don't heap allocate this to avoid making grub_loader_set() fallible. */ +static struct grub_simple_loader_hooks simple_loader_hooks; + struct grub_preboot { grub_err_t (*preboot_func) (int); @@ -44,6 +54,29 @@ static int grub_loader_loaded; static struct grub_preboot *preboots_head = 0, *preboots_tail = 0; +static grub_err_t +grub_simple_boot_hook (void *context) +{ + struct grub_simple_loader_hooks *hooks; + + hooks = (struct grub_simple_loader_hooks *) context; + return hooks->boot (); +} + +static grub_err_t +grub_simple_unload_hook (void *context) +{ + struct grub_simple_loader_hooks *hooks; + grub_err_t ret; + + hooks = (struct grub_simple_loader_hooks *) context; + + ret = hooks->unload (); + grub_memset (hooks, 0, sizeof (*hooks)); + + return ret; +} + int grub_loader_is_loaded (void) { @@ -110,28 +143,45 @@ grub_loader_unregister_preboot_hook (struct grub_preboot *hnd) } void -grub_loader_set (grub_err_t (*boot) (void), - grub_err_t (*unload) (void), - int flags) +grub_loader_set_ex (grub_err_t (*boot) (void *context), + grub_err_t (*unload) (void *context), + void *context, + int flags) { if (grub_loader_loaded && grub_loader_unload_func) - grub_loader_unload_func (); + grub_loader_unload_func (grub_loader_context); grub_loader_boot_func = boot; grub_loader_unload_func = unload; + grub_loader_context = context; grub_loader_flags = flags; grub_loader_loaded = 1; } +void +grub_loader_set (grub_err_t (*boot) (void), + grub_err_t (*unload) (void), + int flags) +{ + grub_loader_set_ex (grub_simple_boot_hook, + grub_simple_unload_hook, + &simple_loader_hooks, + flags); + + simple_loader_hooks.boot = boot; + simple_loader_hooks.unload = unload; +} + void grub_loader_unset(void) { if (grub_loader_loaded && grub_loader_unload_func) - grub_loader_unload_func (); + grub_loader_unload_func (grub_loader_context); grub_loader_boot_func = 0; grub_loader_unload_func = 0; + grub_loader_context = 0; grub_loader_loaded = 0; } @@ -158,7 +208,7 @@ grub_loader_boot (void) return err; } } - err = (grub_loader_boot_func) (); + err = (grub_loader_boot_func) (grub_loader_context); for (cur = preboots_tail; cur; cur = cur->prev) if (! err) diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index a8d7b91..93a028a 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -44,33 +44,28 @@ GRUB_MOD_LICENSE ("GPLv3+"); static grub_dl_t my_mod; -static grub_efi_physical_address_t address; -static grub_efi_uintn_t pages; -static grub_efi_device_path_t *file_path; -static grub_efi_handle_t image_handle; -static grub_efi_char16_t *cmdline; - static grub_err_t -grub_chainloader_unload (void) +grub_chainloader_unload (void *context) { + grub_efi_handle_t image_handle = (grub_efi_handle_t) context; + grub_efi_loaded_image_t *loaded_image; grub_efi_boot_services_t *b; + loaded_image = grub_efi_get_loaded_image (image_handle); + if (loaded_image != NULL) + grub_free (loaded_image->load_options); + b = grub_efi_system_table->boot_services; efi_call_1 (b->unload_image, image_handle); - efi_call_2 (b->free_pages, address, pages); - - grub_free (file_path); - grub_free (cmdline); - cmdline = 0; - file_path = 0; grub_dl_unref (my_mod); return GRUB_ERR_NONE; } static grub_err_t -grub_chainloader_boot (void) +grub_chainloader_boot (void *context) { + grub_efi_handle_t image_handle = (grub_efi_handle_t) context; grub_efi_boot_services_t *b; grub_efi_status_t status; grub_efi_uintn_t exit_data_size; @@ -139,7 +134,7 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename) char *dir_start; char *dir_end; grub_size_t size; - grub_efi_device_path_t *d; + grub_efi_device_path_t *d, *file_path; dir_start = grub_strchr (filename, ')'); if (! dir_start) @@ -215,11 +210,15 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_efi_status_t status; grub_efi_boot_services_t *b; grub_device_t dev = 0; - grub_efi_device_path_t *dp = 0; + grub_efi_device_path_t *dp = NULL, *file_path = NULL; grub_efi_loaded_image_t *loaded_image; char *filename; void *boot_image = 0; grub_efi_handle_t dev_handle = 0; + grub_efi_physical_address_t address = 0; + grub_efi_uintn_t pages = 0; + grub_efi_char16_t *cmdline = NULL; + grub_efi_handle_t image_handle = NULL; if (argc == 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); @@ -227,11 +226,6 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_dl_ref (my_mod); - /* Initialize some global variables. */ - address = 0; - image_handle = 0; - file_path = 0; - b = grub_efi_system_table->boot_services; file = grub_file_open (filename, GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE); @@ -401,7 +395,11 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_file_close (file); grub_device_close (dev); - grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0); + /* We're finished with the source image buffer and file path now. */ + efi_call_2 (b->free_pages, address, pages); + grub_free (file_path); + + grub_loader_set_ex (grub_chainloader_boot, grub_chainloader_unload, image_handle, 0); return 0; fail: @@ -412,11 +410,15 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), if (file) grub_file_close (file); + grub_free (cmdline); grub_free (file_path); if (address) efi_call_2 (b->free_pages, address, pages); + if (image_handle != NULL) + efi_call_1 (b->unload_image, image_handle); + grub_dl_unref (my_mod); return grub_errno; diff --git a/include/grub/loader.h b/include/grub/loader.h index 7f82a49..3071a50 100644 --- a/include/grub/loader.h +++ b/include/grub/loader.h @@ -39,6 +39,11 @@ void EXPORT_FUNC (grub_loader_set) (grub_err_t (*boot) (void), grub_err_t (*unload) (void), int flags); +void EXPORT_FUNC (grub_loader_set_ex) (grub_err_t (*boot) (void *context), + grub_err_t (*unload) (void *context), + void *context, + int flags); + /* Unset current loader, if any. */ void EXPORT_FUNC (grub_loader_unset) (void); -- 2.25.1