Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package grub2 for openSUSE:Factory checked in at 2022-06-09 14:09:19 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/grub2 (Old) and /work/SRC/openSUSE:Factory/.grub2.new.1548 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "grub2" Thu Jun 9 14:09:19 2022 rev:268 rq:981229 version:2.06 Changes: -------- --- /work/SRC/openSUSE:Factory/grub2/grub2.changes 2022-06-03 14:15:15.085206168 +0200 +++ /work/SRC/openSUSE:Factory/.grub2.new.1548/grub2.changes 2022-06-09 14:09:24.364336047 +0200 @@ -1,0 +2,46 @@ +Tue May 31 04:44:18 UTC 2022 - Michael Chang <mch...@suse.com> + +- Security fixes and hardenings for boothole 3 / boothole 2022 (bsc#1198581) + * 0001-video-Remove-trailing-whitespaces.patch + * 0002-loader-efi-chainloader-Simplify-the-loader-state.patch + * 0003-commands-boot-Add-API-to-pass-context-to-loader.patch +- Fix CVE-2022-28736 (bsc#1198496) + * 0004-loader-efi-chainloader-Use-grub_loader_set_ex.patch +- Fix CVE-2022-28735 (bsc#1198495) + * 0005-kern-efi-sb-Reject-non-kernel-files-in-the-shim_lock.patch + * 0006-kern-file-Do-not-leak-device_name-on-error-in-grub_f.patch + * 0007-video-readers-png-Abort-sooner-if-a-read-operation-f.patch + * 0008-video-readers-png-Refuse-to-handle-multiple-image-he.patch +- Fix CVE-2021-3695 (bsc#1191184) + * 0009-video-readers-png-Drop-greyscale-support-to-fix-heap.patch +- Fix CVE-2021-3696 (bsc#1191185) + * 0010-video-readers-png-Avoid-heap-OOB-R-W-inserting-huff-.patch + * 0011-video-readers-png-Sanity-check-some-huffman-codes.patch + * 0012-video-readers-jpeg-Abort-sooner-if-a-read-operation-.patch + * 0013-video-readers-jpeg-Do-not-reallocate-a-given-huff-ta.patch + * 0014-video-readers-jpeg-Refuse-to-handle-multiple-start-o.patch +- Fix CVE-2021-3697 (bsc#1191186) + * 0015-video-readers-jpeg-Block-int-underflow-wild-pointer-.patch + * 0016-normal-charset-Fix-array-out-of-bounds-formatting-un.patch +- Fix CVE-2022-28733 (bsc#1198460) + * 0017-net-ip-Do-IP-fragment-maths-safely.patch + * 0018-net-netbuff-Block-overly-large-netbuff-allocs.patch + * 0019-net-dns-Fix-double-free-addresses-on-corrupt-DNS-res.patch + * 0020-net-dns-Don-t-read-past-the-end-of-the-string-we-re-.patch + * 0021-net-tftp-Prevent-a-UAF-and-double-free-from-a-failed.patch + * 0022-net-tftp-Avoid-a-trivial-UAF.patch + * 0023-net-http-Do-not-tear-down-socket-if-it-s-already-bee.patch +- Fix CVE-2022-28734 (bsc#1198493) + * 0024-net-http-Fix-OOB-write-for-split-http-headers.patch +- Fix CVE-2022-28734 (bsc#1198493) + * 0025-net-http-Error-out-on-headers-with-LF-without-CR.patch + * 0026-fs-f2fs-Do-not-read-past-the-end-of-nat-journal-entr.patch + * 0027-fs-f2fs-Do-not-read-past-the-end-of-nat-bitmap.patch + * 0028-fs-f2fs-Do-not-copy-file-names-that-are-too-long.patch + * 0029-fs-btrfs-Fix-several-fuzz-issues-with-invalid-dir-it.patch + * 0030-fs-btrfs-Fix-more-ASAN-and-SEGV-issues-found-with-fu.patch + * 0031-fs-btrfs-Fix-more-fuzz-issues-related-to-chunks.patch + * 0032-Use-grub_loader_set_ex-for-secureboot-chainloader.patch +- Bump grub's SBAT generation to 2 + +------------------------------------------------------------------- New: ---- 0001-video-Remove-trailing-whitespaces.patch 0002-loader-efi-chainloader-Simplify-the-loader-state.patch 0003-commands-boot-Add-API-to-pass-context-to-loader.patch 0004-loader-efi-chainloader-Use-grub_loader_set_ex.patch 0005-kern-efi-sb-Reject-non-kernel-files-in-the-shim_lock.patch 0006-kern-file-Do-not-leak-device_name-on-error-in-grub_f.patch 0007-video-readers-png-Abort-sooner-if-a-read-operation-f.patch 0008-video-readers-png-Refuse-to-handle-multiple-image-he.patch 0009-video-readers-png-Drop-greyscale-support-to-fix-heap.patch 0010-video-readers-png-Avoid-heap-OOB-R-W-inserting-huff-.patch 0011-video-readers-png-Sanity-check-some-huffman-codes.patch 0012-video-readers-jpeg-Abort-sooner-if-a-read-operation-.patch 0013-video-readers-jpeg-Do-not-reallocate-a-given-huff-ta.patch 0014-video-readers-jpeg-Refuse-to-handle-multiple-start-o.patch 0015-video-readers-jpeg-Block-int-underflow-wild-pointer-.patch 0016-normal-charset-Fix-array-out-of-bounds-formatting-un.patch 0017-net-ip-Do-IP-fragment-maths-safely.patch 0018-net-netbuff-Block-overly-large-netbuff-allocs.patch 0019-net-dns-Fix-double-free-addresses-on-corrupt-DNS-res.patch 0020-net-dns-Don-t-read-past-the-end-of-the-string-we-re-.patch 0021-net-tftp-Prevent-a-UAF-and-double-free-from-a-failed.patch 0022-net-tftp-Avoid-a-trivial-UAF.patch 0023-net-http-Do-not-tear-down-socket-if-it-s-already-bee.patch 0024-net-http-Fix-OOB-write-for-split-http-headers.patch 0025-net-http-Error-out-on-headers-with-LF-without-CR.patch 0026-fs-f2fs-Do-not-read-past-the-end-of-nat-journal-entr.patch 0027-fs-f2fs-Do-not-read-past-the-end-of-nat-bitmap.patch 0028-fs-f2fs-Do-not-copy-file-names-that-are-too-long.patch 0029-fs-btrfs-Fix-several-fuzz-issues-with-invalid-dir-it.patch 0030-fs-btrfs-Fix-more-ASAN-and-SEGV-issues-found-with-fu.patch 0031-fs-btrfs-Fix-more-fuzz-issues-related-to-chunks.patch 0032-Use-grub_loader_set_ex-for-secureboot-chainloader.patch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ grub2.spec ++++++ --- /var/tmp/diff_new_pack.k08Enp/_old 2022-06-09 14:09:27.224339881 +0200 +++ /var/tmp/diff_new_pack.k08Enp/_new 2022-06-09 14:09:27.228339887 +0200 @@ -22,7 +22,7 @@ %if %{defined sbat_distro} # SBAT metadata %define sbat_generation 1 -%define sbat_generation_grub 1 +%define sbat_generation_grub 2 %else %{error please define sbat_distro, sbat_distro_summary and sbat_distro_url} %endif @@ -375,6 +375,38 @@ Patch850: 0001-Fix-infinite-boot-loop-on-headless-system-in-qemu.patch Patch851: 0001-libc-config-merge-from-glibc.patch Patch852: 0001-ofdisk-improve-boot-time-by-lookup-boot-disk-first.patch +Patch853: 0001-video-Remove-trailing-whitespaces.patch +Patch854: 0002-loader-efi-chainloader-Simplify-the-loader-state.patch +Patch855: 0003-commands-boot-Add-API-to-pass-context-to-loader.patch +Patch856: 0004-loader-efi-chainloader-Use-grub_loader_set_ex.patch +Patch857: 0005-kern-efi-sb-Reject-non-kernel-files-in-the-shim_lock.patch +Patch858: 0006-kern-file-Do-not-leak-device_name-on-error-in-grub_f.patch +Patch859: 0007-video-readers-png-Abort-sooner-if-a-read-operation-f.patch +Patch860: 0008-video-readers-png-Refuse-to-handle-multiple-image-he.patch +Patch861: 0009-video-readers-png-Drop-greyscale-support-to-fix-heap.patch +Patch862: 0010-video-readers-png-Avoid-heap-OOB-R-W-inserting-huff-.patch +Patch863: 0011-video-readers-png-Sanity-check-some-huffman-codes.patch +Patch864: 0012-video-readers-jpeg-Abort-sooner-if-a-read-operation-.patch +Patch865: 0013-video-readers-jpeg-Do-not-reallocate-a-given-huff-ta.patch +Patch866: 0014-video-readers-jpeg-Refuse-to-handle-multiple-start-o.patch +Patch867: 0015-video-readers-jpeg-Block-int-underflow-wild-pointer-.patch +Patch868: 0016-normal-charset-Fix-array-out-of-bounds-formatting-un.patch +Patch869: 0017-net-ip-Do-IP-fragment-maths-safely.patch +Patch870: 0018-net-netbuff-Block-overly-large-netbuff-allocs.patch +Patch871: 0019-net-dns-Fix-double-free-addresses-on-corrupt-DNS-res.patch +Patch872: 0020-net-dns-Don-t-read-past-the-end-of-the-string-we-re-.patch +Patch873: 0021-net-tftp-Prevent-a-UAF-and-double-free-from-a-failed.patch +Patch874: 0022-net-tftp-Avoid-a-trivial-UAF.patch +Patch875: 0023-net-http-Do-not-tear-down-socket-if-it-s-already-bee.patch +Patch876: 0024-net-http-Fix-OOB-write-for-split-http-headers.patch +Patch877: 0025-net-http-Error-out-on-headers-with-LF-without-CR.patch +Patch878: 0026-fs-f2fs-Do-not-read-past-the-end-of-nat-journal-entr.patch +Patch879: 0027-fs-f2fs-Do-not-read-past-the-end-of-nat-bitmap.patch +Patch880: 0028-fs-f2fs-Do-not-copy-file-names-that-are-too-long.patch +Patch881: 0029-fs-btrfs-Fix-several-fuzz-issues-with-invalid-dir-it.patch +Patch882: 0030-fs-btrfs-Fix-more-ASAN-and-SEGV-issues-found-with-fu.patch +Patch883: 0031-fs-btrfs-Fix-more-fuzz-issues-related-to-chunks.patch +Patch884: 0032-Use-grub_loader_set_ex-for-secureboot-chainloader.patch Requires: gettext-runtime %if 0%{?suse_version} >= 1140 ++++++ 0001-video-Remove-trailing-whitespaces.patch ++++++ ++++ 687 lines (skipped) ++++++ 0002-loader-efi-chainloader-Simplify-the-loader-state.patch ++++++ >From c111176648717645284865e15d7c6713cf29e982 Mon Sep 17 00:00:00 2001 From: Chris Coulson <chris.coul...@canonical.com> Date: Tue, 5 Apr 2022 10:02:04 +0100 Subject: [PATCH 02/32] loader/efi/chainloader: Simplify the loader state The chainloader command retains the source buffer and device path passed to LoadImage(), requiring the unload hook passed to grub_loader_set() to free them. It isn't required to retain this state though - they aren't required by StartImage() or anything else in the boot hook, so clean them up before grub_cmd_chainloader() finishes. Signed-off-by: Chris Coulson <chris.coul...@canonical.com> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/loader/efi/chainloader.c | 37 ++++++++++++++++-------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index 625f1d26da..1ec09a166c 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -53,12 +53,8 @@ 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_ssize_t fsize; -static grub_efi_device_path_t *file_path; static grub_efi_handle_t image_handle; -static grub_efi_char16_t *cmdline; static grub_ssize_t cmdline_len; static grub_efi_handle_t dev_handle; @@ -70,16 +66,16 @@ static grub_efi_status_t (*entry_point) (grub_efi_handle_t image_handle, grub_e static grub_err_t grub_chainloader_unload (void) { + 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; dev_handle = 0; grub_dl_unref (my_mod); @@ -158,7 +154,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) @@ -641,10 +637,13 @@ 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_physical_address_t address = 0; + grub_efi_uintn_t pages = 0; + grub_efi_char16_t *cmdline = NULL; if (argc == 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); @@ -652,10 +651,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; dev_handle = 0; b = grub_efi_system_table->boot_services; @@ -857,6 +852,10 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_file_close (file); grub_device_close (dev); + /* 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 (grub_chainloader_boot, grub_chainloader_unload, 0); return 0; @@ -868,13 +867,17 @@ 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 (cmdline) - grub_free (cmdline); + if (image_handle != NULL) + { + efi_call_1 (b->unload_image, image_handle); + image_handle = NULL; + } grub_dl_unref (my_mod); -- 2.34.1 ++++++ 0003-commands-boot-Add-API-to-pass-context-to-loader.patch ++++++ >From 8bb57923d39f00b6f850cf6138ff5973cfd0d25f Mon Sep 17 00:00:00 2001 From: Chris Coulson <chris.coul...@canonical.com> Date: Tue, 5 Apr 2022 10:58:28 +0100 Subject: [PATCH 03/32] commands/boot: Add API to pass context to loader Loaders rely on global variables for saving context which is consumed in the boot hook and freed in the unload hook. In the case where a loader command is executed twice, calling grub_loader_set() a second time executes the unload hook, but in some cases this runs when the loader's global context has already been updated, resulting in the updated context being freed and potential use-after-free bugs when the boot hook is subsequently called. This adds a new API, grub_loader_set_ex(), which allows a loader to specify context that is passed to its boot and unload hooks. This is an alternative to requiring that loaders call grub_loader_unset() before mutating their global context. Signed-off-by: Chris Coulson <chris.coul...@canonical.com> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/commands/boot.c | 66 ++++++++++++++++++++++++++++++++++----- include/grub/loader.h | 5 +++ 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/grub-core/commands/boot.c b/grub-core/commands/boot.c index bbca81e947..61514788e2 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/include/grub/loader.h b/include/grub/loader.h index b208642821..97f2310545 100644 --- a/include/grub/loader.h +++ b/include/grub/loader.h @@ -40,6 +40,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.34.1 ++++++ 0004-loader-efi-chainloader-Use-grub_loader_set_ex.patch ++++++ >From e0cc6a601865a72cfe316f2cbbaaefcdd2ad8c69 Mon Sep 17 00:00:00 2001 From: Chris Coulson <chris.coul...@canonical.com> Date: Tue, 5 Apr 2022 11:48:58 +0100 Subject: [PATCH 04/32] 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 <chris.coul...@canonical.com> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/loader/efi/chainloader.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index 1ec09a166c..b3e1e89302 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -54,7 +54,6 @@ GRUB_MOD_LICENSE ("GPLv3+"); static grub_dl_t my_mod; static grub_ssize_t fsize; -static grub_efi_handle_t image_handle; static grub_ssize_t cmdline_len; static grub_efi_handle_t dev_handle; @@ -64,8 +63,9 @@ static grub_efi_status_t (*entry_point) (grub_efi_handle_t image_handle, grub_e #endif 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; @@ -83,8 +83,9 @@ grub_chainloader_unload (void) } 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; @@ -644,6 +645,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), 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")); @@ -856,7 +858,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), efi_call_2 (b->free_pages, address, pages); grub_free (file_path); - grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0); + grub_loader_set_ex (grub_chainloader_boot, grub_chainloader_unload, image_handle, 0); return 0; fail: @@ -874,10 +876,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), efi_call_2 (b->free_pages, address, pages); if (image_handle != NULL) - { - efi_call_1 (b->unload_image, image_handle); - image_handle = NULL; - } + efi_call_1 (b->unload_image, image_handle); grub_dl_unref (my_mod); -- 2.34.1 ++++++ 0005-kern-efi-sb-Reject-non-kernel-files-in-the-shim_lock.patch ++++++ >From ee99e452b9c1aafd3eb80592830ae2c6f69eb395 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode <julian.kl...@canonical.com> Date: Thu, 2 Dec 2021 15:03:53 +0100 Subject: [PATCH 05/32] kern/efi/sb: Reject non-kernel files in the shim_lock verifier We must not allow other verifiers to pass things like the GRUB modules. Instead of maintaining a blocklist, maintain an allowlist of things that we do not care about. This allowlist really should be made reusable, and shared by the lockdown verifier, but this is the minimal patch addressing security concerns where the TPM verifier was able to mark modules as verified (or the OpenPGP verifier for that matter), when it should not do so on shim-powered secure boot systems. Fixes: CVE-2022-28735 Signed-off-by: Julian Andres Klode <julian.kl...@canonical.com> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/kern/efi/sb.c | 39 ++++++++++++++++++++++++++++++++++++--- include/grub/verify.h | 1 + 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/grub-core/kern/efi/sb.c b/grub-core/kern/efi/sb.c index c52ec6226a..89c4bb3fd1 100644 --- a/grub-core/kern/efi/sb.c +++ b/grub-core/kern/efi/sb.c @@ -119,10 +119,11 @@ shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)), void **context __attribute__ ((unused)), enum grub_verify_flags *flags) { - *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + *flags = GRUB_VERIFY_FLAGS_NONE; switch (type & GRUB_FILE_TYPE_MASK) { + /* Files we check. */ case GRUB_FILE_TYPE_LINUX_KERNEL: case GRUB_FILE_TYPE_MULTIBOOT_KERNEL: case GRUB_FILE_TYPE_BSD_KERNEL: @@ -130,11 +131,43 @@ shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)), case GRUB_FILE_TYPE_PLAN9_KERNEL: case GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE: *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK; + return GRUB_ERR_NONE; - /* Fall through. */ + /* Files that do not affect secureboot state. */ + case GRUB_FILE_TYPE_NONE: + case GRUB_FILE_TYPE_LOOPBACK: + case GRUB_FILE_TYPE_LINUX_INITRD: + case GRUB_FILE_TYPE_OPENBSD_RAMDISK: + case GRUB_FILE_TYPE_XNU_RAMDISK: + case GRUB_FILE_TYPE_SIGNATURE: + case GRUB_FILE_TYPE_PUBLIC_KEY: + case GRUB_FILE_TYPE_PUBLIC_KEY_TRUST: + case GRUB_FILE_TYPE_PRINT_BLOCKLIST: + case GRUB_FILE_TYPE_TESTLOAD: + case GRUB_FILE_TYPE_GET_SIZE: + case GRUB_FILE_TYPE_FONT: + case GRUB_FILE_TYPE_ZFS_ENCRYPTION_KEY: + case GRUB_FILE_TYPE_CAT: + case GRUB_FILE_TYPE_HEXCAT: + case GRUB_FILE_TYPE_CMP: + case GRUB_FILE_TYPE_HASHLIST: + case GRUB_FILE_TYPE_TO_HASH: + case GRUB_FILE_TYPE_KEYBOARD_LAYOUT: + case GRUB_FILE_TYPE_PIXMAP: + case GRUB_FILE_TYPE_GRUB_MODULE_LIST: + case GRUB_FILE_TYPE_CONFIG: + case GRUB_FILE_TYPE_THEME: + case GRUB_FILE_TYPE_GETTEXT_CATALOG: + case GRUB_FILE_TYPE_FS_SEARCH: + case GRUB_FILE_TYPE_LOADENV: + case GRUB_FILE_TYPE_SAVEENV: + case GRUB_FILE_TYPE_VERIFY_SIGNATURE: + *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + return GRUB_ERR_NONE; + /* Other files. */ default: - return GRUB_ERR_NONE; + return grub_error (GRUB_ERR_ACCESS_DENIED, N_("prohibited by secure boot policy")); } } diff --git a/include/grub/verify.h b/include/grub/verify.h index 6fde244fc6..67448165f4 100644 --- a/include/grub/verify.h +++ b/include/grub/verify.h @@ -24,6 +24,7 @@ enum grub_verify_flags { + GRUB_VERIFY_FLAGS_NONE = 0, GRUB_VERIFY_FLAGS_SKIP_VERIFICATION = 1, GRUB_VERIFY_FLAGS_SINGLE_CHUNK = 2, /* Defer verification to another authority. */ -- 2.34.1 ++++++ 0006-kern-file-Do-not-leak-device_name-on-error-in-grub_f.patch ++++++ >From 77a70351615e0b6e66d663e063e9b4ba8ae129a0 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Fri, 25 Jun 2021 02:19:05 +1000 Subject: [PATCH 06/32] kern/file: Do not leak device_name on error in grub_file_open() If we have an error in grub_file_open() before we free device_name, we will leak it. Free device_name in the error path and null out the pointer in the good path once we free it there. Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/kern/file.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c index 58454458c4..ffdcaba05f 100644 --- a/grub-core/kern/file.c +++ b/grub-core/kern/file.c @@ -79,6 +79,7 @@ grub_file_open (const char *name, enum grub_file_type type) device = grub_device_open (device_name); grub_free (device_name); + device_name = NULL; if (! device) goto fail; @@ -131,6 +132,7 @@ grub_file_open (const char *name, enum grub_file_type type) return file; fail: + grub_free (device_name); if (device) grub_device_close (device); -- 2.34.1 ++++++ 0007-video-readers-png-Abort-sooner-if-a-read-operation-f.patch ++++++ >From 9db9558e1c75d47beca7ba378a99471c57729be5 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Tue, 6 Jul 2021 14:02:55 +1000 Subject: [PATCH 07/32] video/readers/png: Abort sooner if a read operation fails Fuzzing revealed some inputs that were taking a long time, potentially forever, because they did not bail quickly upon encountering an I/O error. Try to catch I/O errors sooner and bail out. Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/video/readers/png.c | 55 ++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c index 54dfedf435..d715c4629f 100644 --- a/grub-core/video/readers/png.c +++ b/grub-core/video/readers/png.c @@ -142,6 +142,7 @@ static grub_uint8_t grub_png_get_byte (struct grub_png_data *data) { grub_uint8_t r; + grub_ssize_t bytes_read = 0; if ((data->inside_idat) && (data->idat_remain == 0)) { @@ -175,7 +176,14 @@ grub_png_get_byte (struct grub_png_data *data) } r = 0; - grub_file_read (data->file, &r, 1); + bytes_read = grub_file_read (data->file, &r, 1); + + if (bytes_read != 1) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: unexpected end of data"); + return 0; + } if (data->inside_idat) data->idat_remain--; @@ -231,15 +239,16 @@ grub_png_decode_image_palette (struct grub_png_data *data, if (len == 0) return GRUB_ERR_NONE; - for (i = 0; 3 * i < len && i < 256; i++) + grub_errno = GRUB_ERR_NONE; + for (i = 0; 3 * i < len && i < 256 && grub_errno == GRUB_ERR_NONE; i++) for (j = 0; j < 3; j++) data->palette[i][j] = grub_png_get_byte (data); - for (i *= 3; i < len; i++) + for (i *= 3; i < len && grub_errno == GRUB_ERR_NONE; i++) grub_png_get_byte (data); grub_png_get_dword (data); - return GRUB_ERR_NONE; + return grub_errno; } static grub_err_t @@ -256,9 +265,13 @@ grub_png_decode_image_header (struct grub_png_data *data) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: invalid image size"); color_bits = grub_png_get_byte (data); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; data->is_16bit = (color_bits == 16); color_type = grub_png_get_byte (data); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; /* According to PNG spec, no other types are valid. */ if ((color_type & ~(PNG_COLOR_MASK_ALPHA | PNG_COLOR_MASK_COLOR)) @@ -340,14 +353,20 @@ grub_png_decode_image_header (struct grub_png_data *data) if (grub_png_get_byte (data) != PNG_COMPRESSION_BASE) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: compression method not supported"); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if (grub_png_get_byte (data) != PNG_FILTER_TYPE_BASE) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: filter method not supported"); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if (grub_png_get_byte (data) != PNG_INTERLACE_NONE) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: interlace method not supported"); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; /* Skip crc checksum. */ grub_png_get_dword (data); @@ -449,7 +468,7 @@ grub_png_get_huff_code (struct grub_png_data *data, struct huff_table *ht) int code, i; code = 0; - for (i = 0; i < ht->max_length; i++) + for (i = 0; i < ht->max_length && grub_errno == GRUB_ERR_NONE; i++) { code = (code << 1) + grub_png_get_bits (data, 1); if (code < ht->maxval[i]) @@ -504,8 +523,14 @@ grub_png_init_dynamic_block (struct grub_png_data *data) grub_uint8_t lens[DEFLATE_HCLEN_MAX]; nl = DEFLATE_HLIT_BASE + grub_png_get_bits (data, 5); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; nd = DEFLATE_HDIST_BASE + grub_png_get_bits (data, 5); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; nb = DEFLATE_HCLEN_BASE + grub_png_get_bits (data, 4); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if ((nl > DEFLATE_HLIT_MAX) || (nd > DEFLATE_HDIST_MAX) || (nb > DEFLATE_HCLEN_MAX)) @@ -533,7 +558,7 @@ grub_png_init_dynamic_block (struct grub_png_data *data) data->dist_offset); prev = 0; - for (i = 0; i < nl + nd; i++) + for (i = 0; i < nl + nd && grub_errno == GRUB_ERR_NONE; i++) { int n, code; struct huff_table *ht; @@ -721,17 +746,21 @@ grub_png_read_dynamic_block (struct grub_png_data *data) len = cplens[n]; if (cplext[n]) len += grub_png_get_bits (data, cplext[n]); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; n = grub_png_get_huff_code (data, &data->dist_table); dist = cpdist[n]; if (cpdext[n]) dist += grub_png_get_bits (data, cpdext[n]); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; pos = data->wp - dist; if (pos < 0) pos += WSIZE; - while (len > 0) + while (len > 0 && grub_errno == GRUB_ERR_NONE) { data->slide[data->wp] = data->slide[pos]; grub_png_output_byte (data, data->slide[data->wp]); @@ -759,7 +788,11 @@ grub_png_decode_image_data (struct grub_png_data *data) int final; cmf = grub_png_get_byte (data); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; flg = grub_png_get_byte (data); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if ((cmf & 0xF) != Z_DEFLATED) return grub_error (GRUB_ERR_BAD_FILE_TYPE, @@ -774,7 +807,11 @@ grub_png_decode_image_data (struct grub_png_data *data) int block_type; final = grub_png_get_bits (data, 1); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; block_type = grub_png_get_bits (data, 2); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; switch (block_type) { @@ -790,7 +827,7 @@ grub_png_decode_image_data (struct grub_png_data *data) grub_png_get_byte (data); grub_png_get_byte (data); - for (i = 0; i < len; i++) + for (i = 0; i < len && grub_errno == GRUB_ERR_NONE; i++) grub_png_output_byte (data, grub_png_get_byte (data)); break; @@ -1045,6 +1082,8 @@ grub_png_decode_png (struct grub_png_data *data) len = grub_png_get_dword (data); type = grub_png_get_dword (data); + if (grub_errno != GRUB_ERR_NONE) + break; data->next_offset = data->file->offset + len + 4; switch (type) -- 2.34.1 ++++++ 0008-video-readers-png-Refuse-to-handle-multiple-image-he.patch ++++++ >From 2ca24e4cc44effd08fe6dfa05ad8417a9b186f42 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Tue, 6 Jul 2021 14:13:40 +1000 Subject: [PATCH 08/32] video/readers/png: Refuse to handle multiple image headers This causes the bitmap to be leaked. Do not permit multiple image headers. Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/video/readers/png.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c index d715c4629f..35ae553c8e 100644 --- a/grub-core/video/readers/png.c +++ b/grub-core/video/readers/png.c @@ -258,6 +258,9 @@ grub_png_decode_image_header (struct grub_png_data *data) int color_bits; enum grub_video_blit_format blt; + if (data->image_width || data->image_height) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: two image headers found"); + data->image_width = grub_png_get_dword (data); data->image_height = grub_png_get_dword (data); -- 2.34.1 ++++++ 0009-video-readers-png-Drop-greyscale-support-to-fix-heap.patch ++++++ >From 7be3f3b1b7be0602056721526878c91d3333f8fd Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Tue, 6 Jul 2021 18:51:35 +1000 Subject: [PATCH 09/32] video/readers/png: Drop greyscale support to fix heap out-of-bounds write A 16-bit greyscale PNG without alpha is processed in the following loop: for (i = 0; i < (data->image_width * data->image_height); i++, d1 += 4, d2 += 2) { d1[R3] = d2[1]; d1[G3] = d2[1]; d1[B3] = d2[1]; } The increment of d1 is wrong. d1 is incremented by 4 bytes per iteration, but there are only 3 bytes allocated for storage. This means that image data will overwrite somewhat-attacker-controlled parts of memory - 3 bytes out of every 4 following the end of the image. This has existed since greyscale support was added in 2013 in commit 3ccf16dff98f (grub-core/video/readers/png.c: Support grayscale). Saving starfield.png as a 16-bit greyscale image without alpha in the gimp and attempting to load it causes grub-emu to crash - I don't think this code has ever worked. Delete all PNG greyscale support. Fixes: CVE-2021-3695 Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/video/readers/png.c | 87 +++-------------------------------- 1 file changed, 7 insertions(+), 80 deletions(-) diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c index 35ae553c8e..a3161e25b6 100644 --- a/grub-core/video/readers/png.c +++ b/grub-core/video/readers/png.c @@ -100,7 +100,7 @@ struct grub_png_data unsigned image_width, image_height; int bpp, is_16bit; - int raw_bytes, is_gray, is_alpha, is_palette; + int raw_bytes, is_alpha, is_palette; int row_bytes, color_bits; grub_uint8_t *image_data; @@ -296,13 +296,13 @@ grub_png_decode_image_header (struct grub_png_data *data) data->bpp = 3; else { - data->is_gray = 1; - data->bpp = 1; + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: color type not supported"); } if ((color_bits != 8) && (color_bits != 16) && (color_bits != 4 - || !(data->is_gray || data->is_palette))) + || !data->is_palette)) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: bit depth must be 8 or 16"); @@ -331,7 +331,7 @@ grub_png_decode_image_header (struct grub_png_data *data) } #ifndef GRUB_CPU_WORDS_BIGENDIAN - if (data->is_16bit || data->is_gray || data->is_palette) + if (data->is_16bit || data->is_palette) #endif { data->image_data = grub_calloc (data->image_height, data->row_bytes); @@ -899,27 +899,8 @@ grub_png_convert_image (struct grub_png_data *data) int shift; int mask = (1 << data->color_bits) - 1; unsigned j; - if (data->is_gray) - { - /* Generic formula is - (0xff * i) / ((1U << data->color_bits) - 1) - but for allowed bit depth of 1, 2 and for it's - equivalent to - (0xff / ((1U << data->color_bits) - 1)) * i - Precompute the multipliers to avoid division. - */ - - const grub_uint8_t multipliers[5] = { 0xff, 0xff, 0x55, 0x24, 0x11 }; - for (i = 0; i < (1U << data->color_bits); i++) - { - grub_uint8_t col = multipliers[data->color_bits] * i; - palette[i][0] = col; - palette[i][1] = col; - palette[i][2] = col; - } - } - else - grub_memcpy (palette, data->palette, 3 << data->color_bits); + + grub_memcpy (palette, data->palette, 3 << data->color_bits); d1c = d1; d2c = d2; for (j = 0; j < data->image_height; j++, d1c += data->image_width * 3, @@ -957,60 +938,6 @@ grub_png_convert_image (struct grub_png_data *data) return; } - if (data->is_gray) - { - switch (data->bpp) - { - case 4: - /* 16-bit gray with alpha. */ - for (i = 0; i < (data->image_width * data->image_height); - i++, d1 += 4, d2 += 4) - { - d1[R4] = d2[3]; - d1[G4] = d2[3]; - d1[B4] = d2[3]; - d1[A4] = d2[1]; - } - break; - case 2: - if (data->is_16bit) - /* 16-bit gray without alpha. */ - { - for (i = 0; i < (data->image_width * data->image_height); - i++, d1 += 4, d2 += 2) - { - d1[R3] = d2[1]; - d1[G3] = d2[1]; - d1[B3] = d2[1]; - } - } - else - /* 8-bit gray with alpha. */ - { - for (i = 0; i < (data->image_width * data->image_height); - i++, d1 += 4, d2 += 2) - { - d1[R4] = d2[1]; - d1[G4] = d2[1]; - d1[B4] = d2[1]; - d1[A4] = d2[0]; - } - } - break; - /* 8-bit gray without alpha. */ - case 1: - for (i = 0; i < (data->image_width * data->image_height); - i++, d1 += 3, d2++) - { - d1[R3] = d2[0]; - d1[G3] = d2[0]; - d1[B3] = d2[0]; - } - break; - } - return; - } - { /* Only copy the upper 8 bit. */ #ifndef GRUB_CPU_WORDS_BIGENDIAN -- 2.34.1 ++++++ 0010-video-readers-png-Avoid-heap-OOB-R-W-inserting-huff-.patch ++++++ >From 8ebb6943eae81d3c31963bbae42d5d1f168c8dd5 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Tue, 6 Jul 2021 23:25:07 +1000 Subject: [PATCH 10/32] video/readers/png: Avoid heap OOB R/W inserting huff table items In fuzzing we observed crashes where a code would attempt to be inserted into a huffman table before the start, leading to a set of heap OOB reads and writes as table entries with negative indices were shifted around and the new code written in. Catch the case where we would underflow the array and bail. Fixes: CVE-2021-3696 Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/video/readers/png.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c index a3161e25b6..d7ed5aa6cf 100644 --- a/grub-core/video/readers/png.c +++ b/grub-core/video/readers/png.c @@ -438,6 +438,13 @@ grub_png_insert_huff_item (struct huff_table *ht, int code, int len) for (i = len; i < ht->max_length; i++) n += ht->maxval[i]; + if (n > ht->num_values) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: out of range inserting huffman table item"); + return; + } + for (i = 0; i < n; i++) ht->values[ht->num_values - i] = ht->values[ht->num_values - i - 1]; -- 2.34.1 ++++++ 0011-video-readers-png-Sanity-check-some-huffman-codes.patch ++++++ >From bbbb410d5d7d29d935e7108c77a0368e4e007a43 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Tue, 6 Jul 2021 19:19:11 +1000 Subject: [PATCH 11/32] video/readers/png: Sanity check some huffman codes ASAN picked up two OOB global reads: we weren't checking if some code values fit within the cplens or cpdext arrays. Check and throw an error if not. Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/video/readers/png.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c index d7ed5aa6cf..7f2ba7849b 100644 --- a/grub-core/video/readers/png.c +++ b/grub-core/video/readers/png.c @@ -753,6 +753,9 @@ grub_png_read_dynamic_block (struct grub_png_data *data) int len, dist, pos; n -= 257; + if (((unsigned int) n) >= ARRAY_SIZE (cplens)) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: invalid huff code"); len = cplens[n]; if (cplext[n]) len += grub_png_get_bits (data, cplext[n]); @@ -760,6 +763,9 @@ grub_png_read_dynamic_block (struct grub_png_data *data) return grub_errno; n = grub_png_get_huff_code (data, &data->dist_table); + if (((unsigned int) n) >= ARRAY_SIZE (cpdist)) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: invalid huff code"); dist = cpdist[n]; if (cpdext[n]) dist += grub_png_get_bits (data, cpdext[n]); -- 2.34.1 ++++++ 0012-video-readers-jpeg-Abort-sooner-if-a-read-operation-.patch ++++++ >From 27134e18072a9dffb2bda6b74cd312be5360baa0 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Mon, 28 Jun 2021 14:16:14 +1000 Subject: [PATCH 12/32] video/readers/jpeg: Abort sooner if a read operation fails Fuzzing revealed some inputs that were taking a long time, potentially forever, because they did not bail quickly upon encountering an I/O error. Try to catch I/O errors sooner and bail out. Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/video/readers/jpeg.c | 86 +++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c index e31602f766..10225abd53 100644 --- a/grub-core/video/readers/jpeg.c +++ b/grub-core/video/readers/jpeg.c @@ -109,9 +109,17 @@ static grub_uint8_t grub_jpeg_get_byte (struct grub_jpeg_data *data) { grub_uint8_t r; + grub_ssize_t bytes_read; r = 0; - grub_file_read (data->file, &r, 1); + bytes_read = grub_file_read (data->file, &r, 1); + + if (bytes_read != 1) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: unexpected end of data"); + return 0; + } return r; } @@ -120,9 +128,17 @@ static grub_uint16_t grub_jpeg_get_word (struct grub_jpeg_data *data) { grub_uint16_t r; + grub_ssize_t bytes_read; r = 0; - grub_file_read (data->file, &r, sizeof (grub_uint16_t)); + bytes_read = grub_file_read (data->file, &r, sizeof (grub_uint16_t)); + + if (bytes_read != sizeof (grub_uint16_t)) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: unexpected end of data"); + return 0; + } return grub_be_to_cpu16 (r); } @@ -135,6 +151,11 @@ grub_jpeg_get_bit (struct grub_jpeg_data *data) if (data->bit_mask == 0) { data->bit_save = grub_jpeg_get_byte (data); + if (grub_errno != GRUB_ERR_NONE) { + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: file read error"); + return 0; + } if (data->bit_save == JPEG_ESC_CHAR) { if (grub_jpeg_get_byte (data) != 0) @@ -143,6 +164,11 @@ grub_jpeg_get_bit (struct grub_jpeg_data *data) "jpeg: invalid 0xFF in data stream"); return 0; } + if (grub_errno != GRUB_ERR_NONE) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: file read error"); + return 0; + } } data->bit_mask = 0x80; } @@ -161,7 +187,7 @@ grub_jpeg_get_number (struct grub_jpeg_data *data, int num) return 0; msb = value = grub_jpeg_get_bit (data); - for (i = 1; i < num; i++) + for (i = 1; i < num && grub_errno == GRUB_ERR_NONE; i++) value = (value << 1) + (grub_jpeg_get_bit (data) != 0); if (!msb) value += 1 - (1 << num); @@ -202,6 +228,8 @@ grub_jpeg_decode_huff_table (struct grub_jpeg_data *data) while (data->file->offset + sizeof (count) + 1 <= next_marker) { id = grub_jpeg_get_byte (data); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; ac = (id >> 4) & 1; id &= 0xF; if (id > 1) @@ -252,6 +280,8 @@ grub_jpeg_decode_quan_table (struct grub_jpeg_data *data) next_marker = data->file->offset; next_marker += grub_jpeg_get_word (data); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if (next_marker > data->file->size) { @@ -263,6 +293,8 @@ grub_jpeg_decode_quan_table (struct grub_jpeg_data *data) <= next_marker) { id = grub_jpeg_get_byte (data); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if (id >= 0x10) /* Upper 4-bit is precision. */ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: only 8-bit precision is supported"); @@ -294,6 +326,9 @@ grub_jpeg_decode_sof (struct grub_jpeg_data *data) next_marker = data->file->offset; next_marker += grub_jpeg_get_word (data); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + if (grub_jpeg_get_byte (data) != 8) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: only 8-bit precision is supported"); @@ -319,6 +354,8 @@ grub_jpeg_decode_sof (struct grub_jpeg_data *data) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid index"); ss = grub_jpeg_get_byte (data); /* Sampling factor. */ + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if (!id) { grub_uint8_t vs, hs; @@ -498,7 +535,7 @@ grub_jpeg_idct_transform (jpeg_data_unit_t du) } } -static void +static grub_err_t grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du) { int h1, h2, qt; @@ -513,6 +550,9 @@ grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du) data->dc_value[id] += grub_jpeg_get_number (data, grub_jpeg_get_huff_code (data, h1)); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + du[0] = data->dc_value[id] * (int) data->quan_table[qt][0]; pos = 1; while (pos < ARRAY_SIZE (data->quan_table[qt])) @@ -527,11 +567,13 @@ grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du) num >>= 4; pos += num; + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + if (pos >= ARRAY_SIZE (jpeg_zigzag_order)) { - grub_error (GRUB_ERR_BAD_FILE_TYPE, - "jpeg: invalid position in zigzag order!?"); - return; + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: invalid position in zigzag order!?"); } du[jpeg_zigzag_order[pos]] = val * (int) data->quan_table[qt][pos]; @@ -539,6 +581,7 @@ grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du) } grub_jpeg_idct_transform (du); + return GRUB_ERR_NONE; } static void @@ -597,7 +640,8 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data) data_offset += grub_jpeg_get_word (data); cc = grub_jpeg_get_byte (data); - + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if (cc != 3 && cc != 1) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: component count must be 1 or 3"); @@ -610,7 +654,8 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data) id = grub_jpeg_get_byte (data) - 1; if ((id < 0) || (id >= 3)) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid index"); - + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; ht = grub_jpeg_get_byte (data); data->comp_index[id][1] = (ht >> 4); data->comp_index[id][2] = (ht & 0xF) + 2; @@ -618,11 +663,14 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data) if ((data->comp_index[id][1] < 0) || (data->comp_index[id][1] > 3) || (data->comp_index[id][2] < 0) || (data->comp_index[id][2] > 3)) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid hufftable index"); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; } grub_jpeg_get_byte (data); /* Skip 3 unused bytes. */ grub_jpeg_get_word (data); - + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; if (data->file->offset != data_offset) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: extra byte in sos"); @@ -640,6 +688,7 @@ grub_jpeg_decode_data (struct grub_jpeg_data *data) { unsigned c1, vb, hb, nr1, nc1; int rst = data->dri; + grub_err_t err = GRUB_ERR_NONE; vb = 8 << data->log_vs; hb = 8 << data->log_hs; @@ -660,17 +709,22 @@ grub_jpeg_decode_data (struct grub_jpeg_data *data) for (r2 = 0; r2 < (1U << data->log_vs); r2++) for (c2 = 0; c2 < (1U << data->log_hs); c2++) - grub_jpeg_decode_du (data, 0, data->ydu[r2 * 2 + c2]); + { + err = grub_jpeg_decode_du (data, 0, data->ydu[r2 * 2 + c2]); + if (err != GRUB_ERR_NONE) + return err; + } if (data->color_components >= 3) { - grub_jpeg_decode_du (data, 1, data->cbdu); - grub_jpeg_decode_du (data, 2, data->crdu); + err = grub_jpeg_decode_du (data, 1, data->cbdu); + if (err != GRUB_ERR_NONE) + return err; + err = grub_jpeg_decode_du (data, 2, data->crdu); + if (err != GRUB_ERR_NONE) + return err; } - if (grub_errno) - return grub_errno; - nr2 = (data->r1 == nr1 - 1) ? (data->image_height - data->r1 * vb) : vb; nc2 = (c1 == nc1 - 1) ? (data->image_width - c1 * hb) : hb; -- 2.34.1 ++++++ 0013-video-readers-jpeg-Do-not-reallocate-a-given-huff-ta.patch ++++++ >From 8f8282090a1d1469bffd0db6a07275882628caeb Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Mon, 28 Jun 2021 14:16:58 +1000 Subject: [PATCH 13/32] video/readers/jpeg: Do not reallocate a given huff table Fix a memory leak where an invalid file could cause us to reallocate memory for a huffman table we had already allocated memory for. Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/video/readers/jpeg.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c index 10225abd53..caa211f06d 100644 --- a/grub-core/video/readers/jpeg.c +++ b/grub-core/video/readers/jpeg.c @@ -245,6 +245,9 @@ grub_jpeg_decode_huff_table (struct grub_jpeg_data *data) n += count[i]; id += ac * 2; + if (data->huff_value[id] != NULL) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: attempt to reallocate huffman table"); data->huff_value[id] = grub_malloc (n); if (grub_errno) return grub_errno; -- 2.34.1 ++++++ 0014-video-readers-jpeg-Refuse-to-handle-multiple-start-o.patch ++++++ >From 9b6026ba4eb26eadc7ddb8df1c49f648efe257c5 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Mon, 28 Jun 2021 14:25:17 +1000 Subject: [PATCH 14/32] video/readers/jpeg: Refuse to handle multiple start of streams An invalid file could contain multiple start of stream blocks, which would cause us to reallocate and leak our bitmap. Refuse to handle multiple start of streams. Additionally, fix a grub_error() call formatting. Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/video/readers/jpeg.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c index caa211f06d..1df1171d78 100644 --- a/grub-core/video/readers/jpeg.c +++ b/grub-core/video/readers/jpeg.c @@ -677,6 +677,9 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data) if (data->file->offset != data_offset) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: extra byte in sos"); + if (*data->bitmap) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: too many start of scan blocks"); + if (grub_video_bitmap_create (data->bitmap, data->image_width, data->image_height, GRUB_VIDEO_BLIT_FORMAT_RGB_888)) @@ -699,8 +702,8 @@ grub_jpeg_decode_data (struct grub_jpeg_data *data) nc1 = (data->image_width + hb - 1) >> (3 + data->log_hs); if (data->bitmap_ptr == NULL) - return grub_error(GRUB_ERR_BAD_FILE_TYPE, - "jpeg: attempted to decode data before start of stream"); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: attempted to decode data before start of stream"); for (; data->r1 < nr1 && (!data->dri || rst); data->r1++, data->bitmap_ptr += (vb * data->image_width - hb * nc1) * 3) -- 2.34.1 ++++++ 0015-video-readers-jpeg-Block-int-underflow-wild-pointer-.patch ++++++ >From 9a6e9ad21eb2f414dce6eaedd41e146a28142101 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Wed, 7 Jul 2021 15:38:19 +1000 Subject: [PATCH 15/32] video/readers/jpeg: Block int underflow -> wild pointer write Certain 1 px wide images caused a wild pointer write in grub_jpeg_ycrcb_to_rgb(). This was caused because in grub_jpeg_decode_data(), we have the following loop: for (; data->r1 < nr1 && (!data->dri || rst); data->r1++, data->bitmap_ptr += (vb * data->image_width - hb * nc1) * 3) We did not check if vb * width >= hb * nc1. On a 64-bit platform, if that turns out to be negative, it will underflow, be interpreted as unsigned 64-bit, then be added to the 64-bit pointer, so we see data->bitmap_ptr jump, e.g.: 0x6180_0000_0480 to 0x6181_0000_0498 ^ ~--- carry has occurred and this pointer is now far away from any object. On a 32-bit platform, it will decrement the pointer, creating a pointer that won't crash but will overwrite random data. Catch the underflow and error out. Fixes: CVE-2021-3697 Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/video/readers/jpeg.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c index 1df1171d78..97a533b24f 100644 --- a/grub-core/video/readers/jpeg.c +++ b/grub-core/video/readers/jpeg.c @@ -23,6 +23,7 @@ #include <grub/mm.h> #include <grub/misc.h> #include <grub/bufio.h> +#include <grub/safemath.h> GRUB_MOD_LICENSE ("GPLv3+"); @@ -693,6 +694,7 @@ static grub_err_t grub_jpeg_decode_data (struct grub_jpeg_data *data) { unsigned c1, vb, hb, nr1, nc1; + unsigned stride_a, stride_b, stride; int rst = data->dri; grub_err_t err = GRUB_ERR_NONE; @@ -705,8 +707,14 @@ grub_jpeg_decode_data (struct grub_jpeg_data *data) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: attempted to decode data before start of stream"); + if (grub_mul(vb, data->image_width, &stride_a) || + grub_mul(hb, nc1, &stride_b) || + grub_sub(stride_a, stride_b, &stride)) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: cannot decode image with these dimensions"); + for (; data->r1 < nr1 && (!data->dri || rst); - data->r1++, data->bitmap_ptr += (vb * data->image_width - hb * nc1) * 3) + data->r1++, data->bitmap_ptr += stride * 3) for (c1 = 0; c1 < nc1 && (!data->dri || rst); c1++, rst--, data->bitmap_ptr += hb * 3) { -- 2.34.1 ++++++ 0016-normal-charset-Fix-array-out-of-bounds-formatting-un.patch ++++++ >From cfe96e5c432b58726047f4d94f106f58855db1e2 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Tue, 13 Jul 2021 13:24:38 +1000 Subject: [PATCH 16/32] normal/charset: Fix array out-of-bounds formatting unicode for display In some cases attempting to display arbitrary binary strings leads to ASAN splats reading the widthspec array out of bounds. Check the index. If it would be out of bounds, return a width of 1. I don't know if that's strictly correct, but we're not really expecting great display of arbitrary binary data, and it's certainly not worse than an OOB read. Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/normal/charset.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/normal/charset.c b/grub-core/normal/charset.c index 4dfcc31078..7a5a7c153c 100644 --- a/grub-core/normal/charset.c +++ b/grub-core/normal/charset.c @@ -395,6 +395,8 @@ grub_unicode_estimate_width (const struct grub_unicode_glyph *c) { if (grub_unicode_get_comb_type (c->base)) return 0; + if (((unsigned long) (c->base >> 3)) >= ARRAY_SIZE (widthspec)) + return 1; if (widthspec[c->base >> 3] & (1 << (c->base & 7))) return 2; else -- 2.34.1 ++++++ 0017-net-ip-Do-IP-fragment-maths-safely.patch ++++++ >From 5c79af15504c599b58a2f3e591850876dfdb5fa2 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Mon, 20 Dec 2021 19:41:21 +1100 Subject: [PATCH 17/32] net/ip: Do IP fragment maths safely We can receive packets with invalid IP fragmentation information. This can lead to rsm->total_len underflowing and becoming very large. Then, in grub_netbuff_alloc(), we add to this very large number, which can cause it to overflow and wrap back around to a small positive number. The allocation then succeeds, but the resulting buffer is too small and subsequent operations can write past the end of the buffer. Catch the underflow here. Fixes: CVE-2022-28733 Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/net/ip.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index 01410798b3..937be87678 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -25,6 +25,7 @@ #include <grub/net/netbuff.h> #include <grub/mm.h> #include <grub/priority_queue.h> +#include <grub/safemath.h> #include <grub/time.h> struct iphdr { @@ -551,7 +552,14 @@ grub_net_recv_ip4_packets (struct grub_net_buff *nb, { rsm->total_len = (8 * (grub_be_to_cpu16 (iph->frags) & OFFSET_MASK) + (nb->tail - nb->data)); - rsm->total_len -= ((iph->verhdrlen & 0xf) * sizeof (grub_uint32_t)); + + if (grub_sub (rsm->total_len, (iph->verhdrlen & 0xf) * sizeof (grub_uint32_t), + &rsm->total_len)) + { + grub_dprintf ("net", "IP reassembly size underflow\n"); + return GRUB_ERR_NONE; + } + rsm->asm_netbuff = grub_netbuff_alloc (rsm->total_len); if (!rsm->asm_netbuff) { -- 2.34.1 ++++++ 0018-net-netbuff-Block-overly-large-netbuff-allocs.patch ++++++ >From bf949ed28a526b7ab137b8804e2ef6239c3061f2 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Tue, 8 Mar 2022 23:47:46 +1100 Subject: [PATCH 18/32] net/netbuff: Block overly large netbuff allocs A netbuff shouldn't be too huge. It's bounded by MTU and TCP segment reassembly. If we are asked to create one that is unreasonably big, refuse. This is a hardening measure: if we hit this code, there's a bug somewhere else that we should catch and fix. This commit: - stops the bug propagating any further. - provides a spot to instrument in e.g. fuzzing to try to catch these bugs. I have put instrumentation (e.g. __builtin_trap() to force a crash) here and have not been able to find any more crashes. Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/net/netbuff.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/grub-core/net/netbuff.c b/grub-core/net/netbuff.c index dbeeefe478..d5e9e9a0d7 100644 --- a/grub-core/net/netbuff.c +++ b/grub-core/net/netbuff.c @@ -79,10 +79,23 @@ grub_netbuff_alloc (grub_size_t len) COMPILE_TIME_ASSERT (NETBUFF_ALIGN % sizeof (grub_properly_aligned_t) == 0); + /* + * The largest size of a TCP packet is 64 KiB, and everything else + * should be a lot smaller - most MTUs are 1500 or less. Cap data + * size at 64 KiB + a buffer. + */ + if (len > 0xffffUL + 0x1000UL) + { + grub_error (GRUB_ERR_BUG, + "attempted to allocate a packet that is too big"); + return NULL; + } + if (len < NETBUFFMINLEN) len = NETBUFFMINLEN; len = ALIGN_UP (len, NETBUFF_ALIGN); + #ifdef GRUB_MACHINE_EMU data = grub_malloc (len + sizeof (*nb)); #else -- 2.34.1 ++++++ 0019-net-dns-Fix-double-free-addresses-on-corrupt-DNS-res.patch ++++++ >From cc43d9a3d77069850f993fbc4ae47c941bf284b9 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Thu, 16 Sep 2021 01:29:54 +1000 Subject: [PATCH 19/32] net/dns: Fix double-free addresses on corrupt DNS response grub_net_dns_lookup() takes as inputs a pointer to an array of addresses ("addresses") for the given name, and pointer to a number of addresses ("naddresses"). grub_net_dns_lookup() is responsible for allocating "addresses", and the caller is responsible for freeing it if "naddresses" > 0. The DNS recv_hook will sometimes set and free the addresses array, for example if the packet is too short: if (ptr + 10 >= nb->tail) { if (!*data->naddresses) grub_free (*data->addresses); grub_netbuff_free (nb); return GRUB_ERR_NONE; } Later on the nslookup command code unconditionally frees the "addresses" array. Normally this is fine: the array is either populated with valid data or is NULL. But in these sorts of error cases it is neither NULL nor valid and we get a double-free. Only free "addresses" if "naddresses" > 0. It looks like the other use of grub_net_dns_lookup() is not affected. Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/net/dns.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c index 906ec7d678..135faac035 100644 --- a/grub-core/net/dns.c +++ b/grub-core/net/dns.c @@ -667,9 +667,11 @@ grub_cmd_nslookup (struct grub_command *cmd __attribute__ ((unused)), grub_net_addr_to_str (&addresses[i], buf); grub_printf ("%s\n", buf); } - grub_free (addresses); if (naddresses) - return GRUB_ERR_NONE; + { + grub_free (addresses); + return GRUB_ERR_NONE; + } return grub_error (GRUB_ERR_NET_NO_DOMAIN, N_("no DNS record found")); } -- 2.34.1 ++++++ 0020-net-dns-Don-t-read-past-the-end-of-the-string-we-re-.patch ++++++ >From e33af61c202972c81aaccdd395d61855e1584f66 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Mon, 20 Dec 2021 21:55:43 +1100 Subject: [PATCH 20/32] net/dns: Don't read past the end of the string we're checking against I don't really understand what's going on here but fuzzing found a bug where we read past the end of check_with. That's a C string, so use grub_strlen() to make sure we don't overread it. Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/net/dns.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c index 135faac035..17961a9f18 100644 --- a/grub-core/net/dns.c +++ b/grub-core/net/dns.c @@ -146,11 +146,18 @@ check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head, int *length, char *set) { const char *readable_ptr = check_with; + int readable_len; const grub_uint8_t *ptr; char *optr = set; int bytes_processed = 0; if (length) *length = 0; + + if (readable_ptr != NULL) + readable_len = grub_strlen (readable_ptr); + else + readable_len = 0; + for (ptr = name_at; ptr < tail && bytes_processed < tail - head + 2; ) { /* End marker. */ @@ -172,13 +179,16 @@ check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head, ptr = head + (((ptr[0] & 0x3f) << 8) | ptr[1]); continue; } - if (readable_ptr && grub_memcmp (ptr + 1, readable_ptr, *ptr) != 0) + if (readable_ptr != NULL && (*ptr > readable_len || grub_memcmp (ptr + 1, readable_ptr, *ptr) != 0)) return 0; if (grub_memchr (ptr + 1, 0, *ptr) || grub_memchr (ptr + 1, '.', *ptr)) return 0; if (readable_ptr) - readable_ptr += *ptr; + { + readable_ptr += *ptr; + readable_len -= *ptr; + } if (readable_ptr && *readable_ptr != '.' && *readable_ptr != 0) return 0; bytes_processed += *ptr + 1; @@ -192,7 +202,10 @@ check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head, if (optr) *optr++ = '.'; if (readable_ptr && *readable_ptr) - readable_ptr++; + { + readable_ptr++; + readable_len--; + } ptr += *ptr + 1; } return 0; -- 2.34.1 ++++++ 0021-net-tftp-Prevent-a-UAF-and-double-free-from-a-failed.patch ++++++ >From 4adbb12d15af04f8f279a6290cd0195e57cc9e69 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Mon, 20 Sep 2021 01:12:24 +1000 Subject: [PATCH 21/32] net/tftp: Prevent a UAF and double-free from a failed seek A malicious tftp server can cause UAFs and a double free. An attempt to read from a network file is handled by grub_net_fs_read(). If the read is at an offset other than the current offset, grub_net_seek_real() is invoked. In grub_net_seek_real(), if a backwards seek cannot be satisfied from the currently received packets, and the underlying transport does not provide a seek method, then grub_net_seek_real() will close and reopen the network protocol layer. For tftp, the ->close() call goes to tftp_close() and frees the tftp_data_t file->data. The file->data pointer is not nulled out after the free. If the ->open() call fails, the file->data will not be reallocated and will continue point to a freed memory block. This could happen from a server refusing to send the requisite ack to the new tftp request, for example. The seek and the read will then fail, but the grub_file continues to exist: the failed seek does not necessarily cause the entire file to be thrown away (e.g. where the file is checked to see if it is gzipped/lzio/xz/etc., a read failure is interpreted as a decompressor passing on the file, not as an invalidation of the entire grub_file_t structure). This means subsequent attempts to read or seek the file will use the old file->data after free. Eventually, the file will be close()d again and file->data will be freed again. Mark a net_fs file that doesn't reopen as broken. Do not permit read() or close() on a broken file (seek is not exposed directly to the file API - it is only called as part of read, so this blocks seeks as well). As an additional defence, null out the ->data pointer if tftp_open() fails. That would have lead to a simple null pointer dereference rather than a mess of UAFs. This may affect other protocols, I haven't checked. Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/net/net.c | 11 +++++++++-- grub-core/net/tftp.c | 1 + include/grub/net.h | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/grub-core/net/net.c b/grub-core/net/net.c index d11672fbee..b8238b7df1 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -1546,7 +1546,8 @@ grub_net_fs_close (grub_file_t file) grub_netbuff_free (file->device->net->packs.first->nb); grub_net_remove_packet (file->device->net->packs.first); } - file->device->net->protocol->close (file); + if (!file->device->net->broken) + file->device->net->protocol->close (file); grub_free (file->device->net->name); return GRUB_ERR_NONE; } @@ -1768,7 +1769,10 @@ grub_net_seek_real (struct grub_file *file, grub_off_t offset) file->device->net->stall = 0; err = file->device->net->protocol->open (file, file->device->net->name); if (err) - return err; + { + file->device->net->broken = 1; + return err; + } grub_net_fs_read_real (file, NULL, offset); return grub_errno; } @@ -1777,6 +1781,9 @@ grub_net_seek_real (struct grub_file *file, grub_off_t offset) static grub_ssize_t grub_net_fs_read (grub_file_t file, char *buf, grub_size_t len) { + if (file->device->net->broken) + return -1; + if (file->offset != file->device->net->offset) { grub_err_t err; diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index f3e7879388..d1afa25352 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -404,6 +404,7 @@ tftp_open (struct grub_file *file, const char *filename) { grub_net_udp_close (data->sock); grub_free (data); + file->data = NULL; return grub_errno; } diff --git a/include/grub/net.h b/include/grub/net.h index cbcae79b1f..8d71ca6cc5 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -277,6 +277,7 @@ typedef struct grub_net grub_fs_t fs; int eof; int stall; + int broken; } *grub_net_t; extern grub_net_t (*EXPORT_VAR (grub_net_open)) (const char *name); -- 2.34.1 ++++++ 0022-net-tftp-Avoid-a-trivial-UAF.patch ++++++ >From 1824df76e0e712917edce83b5be57d485b81a5a7 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Tue, 18 Jan 2022 14:29:20 +1100 Subject: [PATCH 22/32] net/tftp: Avoid a trivial UAF Under tftp errors, we print a tftp error message from the tftp header. However, the tftph pointer is a pointer inside nb, the netbuff. Previously, we were freeing the nb and then dereferencing it. Don't do that, use it and then free it later. This isn't really _bad_ per se, especially as we're single-threaded, but it trips up fuzzers. Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/net/tftp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index d1afa25352..4222d93b6d 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -251,9 +251,9 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), return GRUB_ERR_NONE; case TFTP_ERROR: data->have_oack = 1; - grub_netbuff_free (nb); grub_error (GRUB_ERR_IO, "%s", tftph->u.err.errmsg); grub_error_save (&data->save_err); + grub_netbuff_free (nb); return GRUB_ERR_NONE; default: grub_netbuff_free (nb); -- 2.34.1 ++++++ 0023-net-http-Do-not-tear-down-socket-if-it-s-already-bee.patch ++++++ >From 7ec48289eeae517e14e2c957a90fd95a49741894 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Tue, 1 Mar 2022 23:14:15 +1100 Subject: [PATCH 23/32] net/http: Do not tear down socket if it's already been torn down It's possible for data->sock to get torn down in tcp error handling. If we unconditionally tear it down again we will end up doing writes to an offset of the NULL pointer when we go to tear it down again. Detect if it has been torn down and don't do it again. Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/net/http.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/grub-core/net/http.c b/grub-core/net/http.c index bf838660d9..a77bc4e4b8 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -425,7 +425,7 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) return err; } - for (i = 0; !data->headers_recv && i < 100; i++) + for (i = 0; data->sock && !data->headers_recv && i < 100; i++) { grub_net_tcp_retransmit (); grub_net_poll_cards (300, &data->headers_recv); @@ -433,7 +433,8 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) if (!data->headers_recv) { - grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); + if (data->sock) + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); if (data->err) { char *str = data->errmsg; -- 2.34.1 ++++++ 0024-net-http-Fix-OOB-write-for-split-http-headers.patch ++++++ >From d7374ab1a110a7ddcfa5a0eda9574ebef2220ee1 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Tue, 8 Mar 2022 18:17:03 +1100 Subject: [PATCH 24/32] net/http: Fix OOB write for split http headers GRUB has special code for handling an http header that is split across two packets. The code tracks the end of line by looking for a "\n" byte. The code for split headers has always advanced the pointer just past the end of the line, whereas the code that handles unsplit headers does not advance the pointer. This extra advance causes the length to be one greater, which breaks an assumption in parse_line(), leading to it writing a NUL byte one byte past the end of the buffer where we reconstruct the line from the two packets. It's conceivable that an attacker controlled set of packets could cause this to zero out the first byte of the "next" pointer of the grub_mm_region structure following the current_line buffer. Do not advance the pointer in the split header case. Fixes: CVE-2022-28734 Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/net/http.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/grub-core/net/http.c b/grub-core/net/http.c index a77bc4e4b8..d9d2ade98e 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -193,9 +193,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), int have_line = 1; char *t; ptr = grub_memchr (nb->data, '\n', nb->tail - nb->data); - if (ptr) - ptr++; - else + if (ptr == NULL) { have_line = 0; ptr = (char *) nb->tail; -- 2.34.1 ++++++ 0025-net-http-Error-out-on-headers-with-LF-without-CR.patch ++++++ >From 5a6ca6483123f2290696e7268f875ff72dd841b6 Mon Sep 17 00:00:00 2001 From: Daniel Axtens <d...@axtens.net> Date: Tue, 8 Mar 2022 19:04:40 +1100 Subject: [PATCH 25/32] net/http: Error out on headers with LF without CR In a similar vein to the previous patch, parse_line() would write a NUL byte past the end of the buffer if there was an HTTP header with a LF rather than a CRLF. RFC-2616 says: Many HTTP/1.1 header field values consist of words separated by LWS or special characters. These special characters MUST be in a quoted string to be used within a parameter value (as defined in section 3.6). We don't support quoted sections or continuation lines, etc. If we see an LF that's not part of a CRLF, bail out. Fixes: CVE-2022-28734 Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/net/http.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/grub-core/net/http.c b/grub-core/net/http.c index d9d2ade98e..0472645d12 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -69,7 +69,15 @@ parse_line (grub_file_t file, http_data_t data, char *ptr, grub_size_t len) char *end = ptr + len; while (end > ptr && *(end - 1) == '\r') end--; + + /* LF without CR. */ + if (end == ptr + len) + { + data->errmsg = grub_strdup (_("invalid HTTP header - LF without CR")); + return GRUB_ERR_NONE; + } *end = 0; + /* Trailing CRLF. */ if (data->in_chunk_len == 1) { -- 2.34.1 ++++++ 0026-fs-f2fs-Do-not-read-past-the-end-of-nat-journal-entr.patch ++++++ >From c1013c295f1e32620db302470f126df0c6a0d5a5 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy <sudha...@linux.ibm.com> Date: Wed, 6 Apr 2022 18:03:37 +0530 Subject: [PATCH 26/32] fs/f2fs: Do not read past the end of nat journal entries A corrupt f2fs file system could specify a nat journal entry count that is beyond the maximum NAT_JOURNAL_ENTRIES. Check if the specified nat journal entry count before accessing the array, and throw an error if it is too large. Signed-off-by: Sudhakar Kuppusamy <sudha...@linux.ibm.com> Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/fs/f2fs.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/grub-core/fs/f2fs.c b/grub-core/fs/f2fs.c index 8a9992ca9e..63702214b0 100644 --- a/grub-core/fs/f2fs.c +++ b/grub-core/fs/f2fs.c @@ -632,23 +632,27 @@ get_nat_journal (struct grub_f2fs_data *data) return err; } -static grub_uint32_t -get_blkaddr_from_nat_journal (struct grub_f2fs_data *data, grub_uint32_t nid) +static grub_err_t +get_blkaddr_from_nat_journal (struct grub_f2fs_data *data, grub_uint32_t nid, + grub_uint32_t *blkaddr) { grub_uint16_t n = grub_le_to_cpu16 (data->nat_j.n_nats); - grub_uint32_t blkaddr = 0; grub_uint16_t i; + if (n >= NAT_JOURNAL_ENTRIES) + return grub_error (GRUB_ERR_BAD_FS, + "invalid number of nat journal entries"); + for (i = 0; i < n; i++) { if (grub_le_to_cpu32 (data->nat_j.entries[i].nid) == nid) { - blkaddr = grub_le_to_cpu32 (data->nat_j.entries[i].ne.block_addr); + *blkaddr = grub_le_to_cpu32 (data->nat_j.entries[i].ne.block_addr); break; } } - return blkaddr; + return GRUB_ERR_NONE; } static grub_uint32_t @@ -656,10 +660,13 @@ get_node_blkaddr (struct grub_f2fs_data *data, grub_uint32_t nid) { struct grub_f2fs_nat_block *nat_block; grub_uint32_t seg_off, block_off, entry_off, block_addr; - grub_uint32_t blkaddr; + grub_uint32_t blkaddr = 0; grub_err_t err; - blkaddr = get_blkaddr_from_nat_journal (data, nid); + err = get_blkaddr_from_nat_journal (data, nid, &blkaddr); + if (err != GRUB_ERR_NONE) + return 0; + if (blkaddr) return blkaddr; -- 2.34.1 ++++++ 0027-fs-f2fs-Do-not-read-past-the-end-of-nat-bitmap.patch ++++++ >From 5ac885d02a9e91a5d6760090f90fa2bb4e7a5dd6 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy <sudha...@linux.ibm.com> Date: Wed, 6 Apr 2022 18:49:09 +0530 Subject: [PATCH 27/32] fs/f2fs: Do not read past the end of nat bitmap A corrupt f2fs filesystem could have a block offset or a bitmap offset that would cause us to read beyond the bounds of the nat bitmap. Introduce the nat_bitmap_size member in grub_f2fs_data which holds the size of nat bitmap. Set the size when loading the nat bitmap in nat_bitmap_ptr(), and catch when an invalid offset would create a pointer past the end of the allocated space. Check against the bitmap size in grub_f2fs_test_bit() test bit to avoid reading past the end of the nat bitmap. Signed-off-by: Sudhakar Kuppusamy <sudha...@linux.ibm.com> Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/fs/f2fs.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/grub-core/fs/f2fs.c b/grub-core/fs/f2fs.c index 63702214b0..8898b235e0 100644 --- a/grub-core/fs/f2fs.c +++ b/grub-core/fs/f2fs.c @@ -122,6 +122,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define F2FS_INLINE_DOTS 0x10 /* File having implicit dot dentries. */ #define MAX_VOLUME_NAME 512 +#define MAX_NAT_BITMAP_SIZE 3900 enum FILE_TYPE { @@ -183,7 +184,7 @@ struct grub_f2fs_checkpoint grub_uint32_t checksum_offset; grub_uint64_t elapsed_time; grub_uint8_t alloc_type[MAX_ACTIVE_LOGS]; - grub_uint8_t sit_nat_version_bitmap[3900]; + grub_uint8_t sit_nat_version_bitmap[MAX_NAT_BITMAP_SIZE]; grub_uint32_t checksum; } GRUB_PACKED; @@ -302,6 +303,7 @@ struct grub_f2fs_data struct grub_f2fs_nat_journal nat_j; char *nat_bitmap; + grub_uint32_t nat_bitmap_size; grub_disk_t disk; struct grub_f2fs_node *inode; @@ -377,15 +379,20 @@ sum_blk_addr (struct grub_f2fs_data *data, int base, int type) } static void * -nat_bitmap_ptr (struct grub_f2fs_data *data) +nat_bitmap_ptr (struct grub_f2fs_data *data, grub_uint32_t *nat_bitmap_size) { struct grub_f2fs_checkpoint *ckpt = &data->ckpt; grub_uint32_t offset; + *nat_bitmap_size = MAX_NAT_BITMAP_SIZE; if (grub_le_to_cpu32 (data->sblock.cp_payload) > 0) return ckpt->sit_nat_version_bitmap; offset = grub_le_to_cpu32 (ckpt->sit_ver_bitmap_bytesize); + if (offset >= MAX_NAT_BITMAP_SIZE) + return NULL; + + *nat_bitmap_size = *nat_bitmap_size - offset; return ckpt->sit_nat_version_bitmap + offset; } @@ -438,11 +445,15 @@ grub_f2fs_crc_valid (grub_uint32_t blk_crc, void *buf, const grub_uint32_t len) } static int -grub_f2fs_test_bit (grub_uint32_t nr, const char *p) +grub_f2fs_test_bit (grub_uint32_t nr, const char *p, grub_uint32_t len) { int mask; + grub_uint32_t shifted_nr = (nr >> 3); + + if (shifted_nr >= len) + return -1; - p += (nr >> 3); + p += shifted_nr; mask = 1 << (7 - (nr & 0x07)); return mask & *p; @@ -662,6 +673,7 @@ get_node_blkaddr (struct grub_f2fs_data *data, grub_uint32_t nid) grub_uint32_t seg_off, block_off, entry_off, block_addr; grub_uint32_t blkaddr = 0; grub_err_t err; + int result_bit; err = get_blkaddr_from_nat_journal (data, nid, &blkaddr); if (err != GRUB_ERR_NONE) @@ -682,8 +694,15 @@ get_node_blkaddr (struct grub_f2fs_data *data, grub_uint32_t nid) ((seg_off * data->blocks_per_seg) << 1) + (block_off & (data->blocks_per_seg - 1)); - if (grub_f2fs_test_bit (block_off, data->nat_bitmap)) + result_bit = grub_f2fs_test_bit (block_off, data->nat_bitmap, + data->nat_bitmap_size); + if (result_bit > 0) block_addr += data->blocks_per_seg; + else if (result_bit == -1) + { + grub_free (nat_block); + return 0; + } err = grub_f2fs_block_read (data, block_addr, nat_block); if (err) @@ -833,7 +852,9 @@ grub_f2fs_mount (grub_disk_t disk) if (err) goto fail; - data->nat_bitmap = nat_bitmap_ptr (data); + data->nat_bitmap = nat_bitmap_ptr (data, &data->nat_bitmap_size); + if (data->nat_bitmap == NULL) + goto fail; err = get_nat_journal (data); if (err) -- 2.34.1 ++++++ 0028-fs-f2fs-Do-not-copy-file-names-that-are-too-long.patch ++++++ >From 315e2773ed5cae92548d4508301ac0fe7515e5bb Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy <sudha...@linux.ibm.com> Date: Wed, 6 Apr 2022 18:17:43 +0530 Subject: [PATCH 28/32] fs/f2fs: Do not copy file names that are too long A corrupt f2fs file system might specify a name length which is greater than the maximum name length supported by the GRUB f2fs driver. We will allocate enough memory to store the overly long name, but there are only F2FS_NAME_LEN bytes in the source, so we would read past the end of the source. While checking directory entries, do not copy a file name with an invalid length. Signed-off-by: Sudhakar Kuppusamy <sudha...@linux.ibm.com> Signed-off-by: Daniel Axtens <d...@axtens.net> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/fs/f2fs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/grub-core/fs/f2fs.c b/grub-core/fs/f2fs.c index 8898b235e0..df6beb544c 100644 --- a/grub-core/fs/f2fs.c +++ b/grub-core/fs/f2fs.c @@ -1003,6 +1003,10 @@ grub_f2fs_check_dentries (struct grub_f2fs_dir_iter_ctx *ctx) ftype = ctx->dentry[i].file_type; name_len = grub_le_to_cpu16 (ctx->dentry[i].name_len); + + if (name_len >= F2FS_NAME_LEN) + return 0; + filename = grub_malloc (name_len + 1); if (!filename) return 0; -- 2.34.1 ++++++ 0029-fs-btrfs-Fix-several-fuzz-issues-with-invalid-dir-it.patch ++++++ >From c64e0158654a1098caf652f6ffd192cbe26583f3 Mon Sep 17 00:00:00 2001 From: Darren Kenny <darren.ke...@oracle.com> Date: Tue, 29 Mar 2022 10:49:56 +0000 Subject: [PATCH 29/32] fs/btrfs: Fix several fuzz issues with invalid dir item sizing According to the btrfs code in Linux, the structure of a directory item leaf should be of the form: |struct btrfs_dir_item|name|data| in GRUB the name len and data len are in the grub_btrfs_dir_item structure's n and m fields respectively. The combined size of the structure, name and data should be less than the allocated memory, a difference to the Linux kernel's struct btrfs_dir_item is that the grub_btrfs_dir_item has an extra field for where the name is stored, so we adjust for that too. Signed-off-by: Darren Kenny <darren.ke...@oracle.com> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/fs/btrfs.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 42fdbaf616..626fd2daa0 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -2210,6 +2210,7 @@ grub_btrfs_dir (grub_device_t device, const char *path, grub_uint64_t tree; grub_uint8_t type; char *new_path = NULL; + grub_size_t est_size = 0; if (!data) return grub_errno; @@ -2276,6 +2277,18 @@ grub_btrfs_dir (grub_device_t device, const char *path, break; } + if (direl == NULL || + grub_add (grub_le_to_cpu16 (direl->n), + grub_le_to_cpu16 (direl->m), &est_size) || + grub_add (est_size, sizeof (*direl), &est_size) || + grub_sub (est_size, sizeof (direl->name), &est_size) || + est_size > allocated) + { + grub_errno = GRUB_ERR_OUT_OF_RANGE; + r = -grub_errno; + goto out; + } + for (cdirel = direl; (grub_uint8_t *) cdirel - (grub_uint8_t *) direl < (grub_ssize_t) elemsize; @@ -2286,6 +2299,19 @@ grub_btrfs_dir (grub_device_t device, const char *path, char c; struct grub_btrfs_inode inode; struct grub_dirhook_info info; + + if (cdirel == NULL || + grub_add (grub_le_to_cpu16 (cdirel->n), + grub_le_to_cpu16 (cdirel->m), &est_size) || + grub_add (est_size, sizeof (*cdirel), &est_size) || + grub_sub (est_size, sizeof (cdirel->name), &est_size) || + est_size > allocated) + { + grub_errno = GRUB_ERR_OUT_OF_RANGE; + r = -grub_errno; + goto out; + } + err = grub_btrfs_read_inode (data, &inode, cdirel->key.object_id, tree); grub_memset (&info, 0, sizeof (info)); -- 2.34.1 ++++++ 0030-fs-btrfs-Fix-more-ASAN-and-SEGV-issues-found-with-fu.patch ++++++ >From 2576115cc77c45d2a77d7629b8c2f26a3a58822b Mon Sep 17 00:00:00 2001 From: Darren Kenny <darren.ke...@oracle.com> Date: Tue, 29 Mar 2022 15:52:46 +0000 Subject: [PATCH 30/32] fs/btrfs: Fix more ASAN and SEGV issues found with fuzzing The fuzzer is generating btrfs file systems that have chunks with invalid combinations of stripes and substripes for the given RAID configurations. After examining the Linux kernel fs/btrfs/tree-checker.c code, it appears that sub-stripes should only be applied to RAID10, and in that case there should only ever be 2 of them. Similarly, RAID single should only have 1 stripe, and RAID1/1C3/1C4 should have 2. 3 or 4 stripes respectively, which is what redundancy corresponds. Some of the chunks ended up with a size of 0, which grub_malloc() still returned memory for and in turn generated ASAN errors later when accessed. While it would be possible to specifically limit the number of stripes, a more correct test was on the combination of the chunk item, and the number of stripes by the size of the chunk stripe structure in comparison to the size of the chunk itself. Signed-off-by: Darren Kenny <darren.ke...@oracle.com> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/fs/btrfs.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 626fd2daa0..62fe5e6a69 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -938,6 +938,12 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, return grub_error (GRUB_ERR_BAD_FS, "couldn't find the chunk descriptor"); + if (!chsize) + { + grub_dprintf ("btrfs", "zero-size chunk\n"); + return grub_error (GRUB_ERR_BAD_FS, + "got an invalid zero-size chunk"); + } chunk = grub_malloc (chsize); if (!chunk) return grub_errno; @@ -996,6 +1002,16 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, stripe_length = grub_divmod64 (grub_le_to_cpu64 (chunk->size), nstripes, NULL); + + /* For single, there should be exactly 1 stripe. */ + if (grub_le_to_cpu16 (chunk->nstripes) != 1) + { + grub_dprintf ("btrfs", "invalid RAID_SINGLE: nstripes != 1 (%u)\n", + grub_le_to_cpu16 (chunk->nstripes)); + return grub_error (GRUB_ERR_BAD_FS, + "invalid RAID_SINGLE: nstripes != 1 (%u)", + grub_le_to_cpu16 (chunk->nstripes)); + } if (stripe_length == 0) stripe_length = 512; stripen = grub_divmod64 (off, stripe_length, &stripe_offset); @@ -1015,6 +1031,19 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, stripen = 0; stripe_offset = off; csize = grub_le_to_cpu64 (chunk->size) - off; + + /* + * Redundancy, and substripes only apply to RAID10, and there + * should be exactly 2 sub-stripes. + */ + if (grub_le_to_cpu16 (chunk->nstripes) != redundancy) + { + grub_dprintf ("btrfs", "invalid RAID1: nstripes != %u (%u)\n", + redundancy, grub_le_to_cpu16 (chunk->nstripes)); + return grub_error (GRUB_ERR_BAD_FS, + "invalid RAID1: nstripes != %u (%u)", + redundancy, grub_le_to_cpu16 (chunk->nstripes)); + } break; } case GRUB_BTRFS_CHUNK_TYPE_RAID0: @@ -1051,6 +1080,20 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, stripe_offset = low + chunk_stripe_length * high; csize = chunk_stripe_length - low; + + /* + * Substripes only apply to RAID10, and there + * should be exactly 2 sub-stripes. + */ + if (grub_le_to_cpu16 (chunk->nsubstripes) != 2) + { + grub_dprintf ("btrfs", "invalid RAID10: nsubstripes != 2 (%u)", + grub_le_to_cpu16 (chunk->nsubstripes)); + return grub_error (GRUB_ERR_BAD_FS, + "invalid RAID10: nsubstripes != 2 (%u)", + grub_le_to_cpu16 (chunk->nsubstripes)); + } + break; } case GRUB_BTRFS_CHUNK_TYPE_RAID5: @@ -1150,6 +1193,8 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, for (j = 0; j < 2; j++) { + grub_size_t est_chunk_alloc = 0; + grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T "+0x%" PRIxGRUB_UINT64_T " (%d stripes (%d substripes) of %" @@ -1162,6 +1207,16 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, grub_dprintf ("btrfs", "reading laddr 0x%" PRIxGRUB_UINT64_T "\n", addr); + if (grub_mul (sizeof (struct grub_btrfs_chunk_stripe), + grub_le_to_cpu16 (chunk->nstripes), &est_chunk_alloc) || + grub_add (est_chunk_alloc, + sizeof (struct grub_btrfs_chunk_item), &est_chunk_alloc) || + est_chunk_alloc > chunk->size) + { + err = GRUB_ERR_BAD_FS; + break; + } + if (is_raid56) { err = btrfs_read_from_chunk (data, chunk, stripen, -- 2.34.1 ++++++ 0031-fs-btrfs-Fix-more-fuzz-issues-related-to-chunks.patch ++++++ >From 480019e546386b8e25a26d03408897a7752b98b6 Mon Sep 17 00:00:00 2001 From: Darren Kenny <darren.ke...@oracle.com> Date: Thu, 7 Apr 2022 15:18:12 +0000 Subject: [PATCH 31/32] fs/btrfs: Fix more fuzz issues related to chunks The corpus was generating issues in grub_btrfs_read_logical() when attempting to iterate over stripe entries in the superblock's bootmapping. In most cases the reason for the failure was that the number of stripes in chunk->nstripes exceeded the possible space statically allocated in superblock bootmapping space. Each stripe entry in the bootmapping block consists of a grub_btrfs_key followed by a grub_btrfs_chunk_stripe. Another issue that came up was that while calculating the chunk size, in an earlier piece of code in that function, depending on the data provided in the btrfs file system, it would end up calculating a size that was too small to contain even 1 grub_btrfs_chunk_item, which is obviously invalid too. Signed-off-by: Darren Kenny <darren.ke...@oracle.com> Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com> --- grub-core/fs/btrfs.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 62fe5e6a69..7007463c6e 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -944,6 +944,17 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, return grub_error (GRUB_ERR_BAD_FS, "got an invalid zero-size chunk"); } + + /* + * The space being allocated for a chunk should at least be able to + * contain one chunk item. + */ + if (chsize < sizeof (struct grub_btrfs_chunk_item)) + { + grub_dprintf ("btrfs", "chunk-size too small\n"); + return grub_error (GRUB_ERR_BAD_FS, + "got an invalid chunk size"); + } chunk = grub_malloc (chsize); if (!chunk) return grub_errno; @@ -1191,6 +1202,13 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, if (csize > (grub_uint64_t) size) csize = size; + /* + * The space for a chunk stripe is limited to the space provide in the super-block's + * bootstrap mapping with an initial btrfs key at the start of each chunk. + */ + grub_size_t avail_stripes = sizeof (data->sblock.bootstrap_mapping) / + (sizeof (struct grub_btrfs_key) + sizeof (struct grub_btrfs_chunk_stripe)); + for (j = 0; j < 2; j++) { grub_size_t est_chunk_alloc = 0; @@ -1217,6 +1235,12 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, break; } + if (grub_le_to_cpu16 (chunk->nstripes) > avail_stripes) + { + err = GRUB_ERR_BAD_FS; + break; + } + if (is_raid56) { err = btrfs_read_from_chunk (data, chunk, stripen, -- 2.34.1 ++++++ 0032-Use-grub_loader_set_ex-for-secureboot-chainloader.patch ++++++ >From 836337d9b895da32bcbc451c84bc3a7865a15963 Mon Sep 17 00:00:00 2001 From: Michael Chang <mch...@suse.com> Date: Mon, 18 Apr 2022 22:16:49 +0800 Subject: [PATCH 32/32] Use grub_loader_set_ex() for secureboot chainloader This is required as many distributions, including SUSE, has been shipping a variation to load and start image using native functions than calling out efi protocols when secure boot is enabled and shim lock is used to verify image. Signed-off-by: Michael Chang <mch...@suse.com> --- grub-core/loader/efi/chainloader.c | 100 +++++++++++++++++++---------- 1 file changed, 66 insertions(+), 34 deletions(-) diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index b3e1e89302..48d69c7795 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -53,10 +53,6 @@ GRUB_MOD_LICENSE ("GPLv3+"); static grub_dl_t my_mod; -static grub_ssize_t fsize; -static grub_ssize_t cmdline_len; -static grub_efi_handle_t dev_handle; - #ifdef SUPPORT_SECURE_BOOT static grub_efi_boolean_t debug_secureboot = 0; static grub_efi_status_t (*entry_point) (grub_efi_handle_t image_handle, grub_efi_system_table_t *system_table); @@ -76,8 +72,6 @@ grub_chainloader_unload (void *context) b = grub_efi_system_table->boot_services; efi_call_1 (b->unload_image, image_handle); - dev_handle = 0; - grub_dl_unref (my_mod); return GRUB_ERR_NONE; } @@ -254,6 +248,17 @@ struct pe_coff_loader_image_context struct grub_pe32_header_no_msdos_stub *pe_hdr; }; +struct grub_secureboot_chainloader_context +{ + grub_efi_physical_address_t address; + grub_efi_uintn_t pages; + grub_ssize_t fsize; + grub_efi_device_path_t *file_path; + grub_efi_char16_t *cmdline; + grub_ssize_t cmdline_len; + grub_efi_handle_t dev_handle; +}; + typedef struct pe_coff_loader_image_context pe_coff_loader_image_context_t; struct grub_efi_shim_lock @@ -477,11 +482,13 @@ grub_efi_get_media_file_path (grub_efi_device_path_t *dp) } static grub_efi_boolean_t -handle_image (void *data, grub_efi_uint32_t datasize) +handle_image (struct grub_secureboot_chainloader_context *load_context) { grub_efi_boot_services_t *b; grub_efi_loaded_image_t *li, li_bak; grub_efi_status_t efi_status; + void *data = (void *)(unsigned long)load_context->address; + grub_efi_uint32_t datasize = load_context->fsize; char *buffer = NULL; char *buffer_aligned = NULL; grub_efi_uint32_t i, size; @@ -571,10 +578,10 @@ handle_image (void *data, grub_efi_uint32_t datasize) grub_memcpy (&li_bak, li, sizeof (grub_efi_loaded_image_t)); li->image_base = buffer_aligned; li->image_size = context.image_size; - li->load_options = cmdline; - li->load_options_size = cmdline_len; - li->file_path = grub_efi_get_media_file_path (file_path); - li->device_handle = dev_handle; + li->load_options = load_context->cmdline; + li->load_options_size = load_context->cmdline_len; + li->file_path = grub_efi_get_media_file_path (load_context->file_path); + li->device_handle = load_context->dev_handle; if (li->file_path) { grub_printf ("file path: "); @@ -605,26 +612,27 @@ error_exit: } static grub_err_t -grub_secureboot_chainloader_unload (void) +grub_secureboot_chainloader_unload (void* context) { grub_efi_boot_services_t *b; + struct grub_secureboot_chainloader_context *sb_context = (struct grub_secureboot_chainloader_context *)context; b = grub_efi_system_table->boot_services; - efi_call_2 (b->free_pages, address, pages); - grub_free (file_path); - grub_free (cmdline); - cmdline = 0; - file_path = 0; - dev_handle = 0; + efi_call_2 (b->free_pages, sb_context->address, sb_context->pages); + grub_free (sb_context->file_path); + grub_free (sb_context->cmdline); + grub_free (sb_context); grub_dl_unref (my_mod); return GRUB_ERR_NONE; } static grub_err_t -grub_secureboot_chainloader_boot (void) +grub_secureboot_chainloader_boot (void *context) { - handle_image ((void *)address, fsize); + struct grub_secureboot_chainloader_context *sb_context = (struct grub_secureboot_chainloader_context *)context; + + handle_image (sb_context); grub_loader_unset (); return grub_errno; } @@ -635,6 +643,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { grub_file_t file = 0; + grub_ssize_t size; grub_efi_status_t status; grub_efi_boot_services_t *b; grub_device_t dev = 0; @@ -646,6 +655,8 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_efi_uintn_t pages = 0; grub_efi_char16_t *cmdline = NULL; grub_efi_handle_t image_handle = NULL; + grub_ssize_t cmdline_len = 0; + grub_efi_handle_t dev_handle = 0; if (argc == 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); @@ -653,8 +664,6 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_dl_ref (my_mod); - dev_handle = 0; - b = grub_efi_system_table->boot_services; if (argc > 1) @@ -732,14 +741,14 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_printf ("file path: "); grub_efi_print_device_path (file_path); - fsize = grub_file_size (file); - if (!fsize) + size = grub_file_size (file); + if (!size) { grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), filename); goto fail; } - pages = (((grub_efi_uintn_t) fsize + ((1 << 12) - 1)) >> 12); + pages = (((grub_efi_uintn_t) size + ((1 << 12) - 1)) >> 12); status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_ANY_PAGES, GRUB_EFI_LOADER_CODE, @@ -753,7 +762,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), } boot_image = (void *) ((grub_addr_t) address); - if (grub_file_read (file, boot_image, fsize) != fsize) + if (grub_file_read (file, boot_image, size) != size) { if (grub_errno == GRUB_ERR_NONE) grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), @@ -763,7 +772,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), } #if defined (__i386__) || defined (__x86_64__) - if (fsize >= (grub_ssize_t) sizeof (struct grub_macho_fat_header)) + if (size >= (grub_ssize_t) sizeof (struct grub_macho_fat_header)) { struct grub_macho_fat_header *head = boot_image; if (head->magic @@ -786,30 +795,42 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), > ~grub_cpu_to_le32 (archs[i].size) || grub_cpu_to_le32 (archs[i].offset) + grub_cpu_to_le32 (archs[i].size) - > (grub_size_t) fsize) + > (grub_size_t) size) { grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), filename); goto fail; } boot_image = (char *) boot_image + grub_cpu_to_le32 (archs[i].offset); - fsize = grub_cpu_to_le32 (archs[i].size); + size = grub_cpu_to_le32 (archs[i].size); } } #endif #ifdef SUPPORT_SECURE_BOOT /* FIXME is secure boot possible also with universal binaries? */ - if (debug_secureboot || (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED && grub_secure_validate ((void *)address, fsize))) + if (debug_secureboot || (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED && grub_secure_validate ((void *)address, size))) { + struct grub_secureboot_chainloader_context *sb_context; + + sb_context = grub_malloc (sizeof (*sb_context)); + if (!sb_context) + goto fail; + sb_context->cmdline = cmdline; + sb_context->cmdline_len = cmdline_len; + sb_context->fsize = size; + sb_context->dev_handle = dev_handle; + sb_context->address = address; + sb_context->pages = pages; + sb_context->file_path = file_path; grub_file_close (file); - grub_loader_set (grub_secureboot_chainloader_boot, grub_secureboot_chainloader_unload, 0); + grub_loader_set_ex (grub_secureboot_chainloader_boot, grub_secureboot_chainloader_unload, sb_context, 0); return 0; } #endif status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path, - boot_image, fsize, + boot_image, size, &image_handle); #ifdef SUPPORT_SECURE_BOOT if (status == GRUB_EFI_SECURITY_VIOLATION && grub_efi_get_secureboot () != GRUB_EFI_SECUREBOOT_MODE_ENABLED) @@ -817,10 +838,21 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), /* If it failed with security violation while not in secure boot mode, the firmware might be broken. We try to workaround on that by forcing the SB method! (bsc#887793) */ + struct grub_secureboot_chainloader_context *sb_context; + grub_dprintf ("chain", "Possible firmware flaw! Security violation while not in secure boot mode.\n"); + sb_context = grub_malloc (sizeof (*sb_context)); + if (!sb_context) + goto fail; + sb_context->cmdline = cmdline; + sb_context->cmdline_len = cmdline_len; + sb_context->fsize = size; + sb_context->dev_handle = dev_handle; + sb_context->address = address; + sb_context->pages = pages; grub_file_close (file); - grub_loader_set (grub_secureboot_chainloader_boot, - grub_secureboot_chainloader_unload, 0); + grub_loader_set_ex (grub_secureboot_chainloader_boot, + grub_secureboot_chainloader_unload, sb_context, 0); return 0; } #endif -- 2.34.1