From f326c5c4753c36a9068ba66036566229a0975908 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Fri, 25 Jul 2025 14:47:05 +0100 Subject: [PATCH 001/208] commands/bli: Set LoaderTpm2ActivePcrBanks runtime variable It turns out checking from userspace is not 100% reliable to figure out whether the firmware had TPM2 support enabled or not. For example with EDK2 arm64, the default upstream build config bundles TPM2 support with SecureBoot support, so if the latter is disabled, TPM2 is also unavailable. But still, the ACPI TPM2 table is created just as if it was enabled. So, /sys/firmware/acpi/tables/TPM2 exists and looks correct but there are no measurements, neither the firmware nor the loader/stub can do them, and /sys/kernel/security/tpm0/binary_bios_measurements does not exist. So, userspace cannot really tell what was going on in UEFI mode. The loader can use the apposite UEFI protocol to check, which is a more definitive answer. Export the bitmask with the list of active banks as-is. If it's not 0, then in userspace we can be sure a working TPM2 was available in UEFI mode. systemd-boot and systemd-stub v258 (current main) set this variable and userspace portion consumes it to be able to tell what was available in the firmware context. Signed-off-by: Luca Boccassi Reviewed-by: Daniel Kiper --- grub-core/commands/bli.c | 23 +++++++++++++++++++++++ grub-core/commands/efi/tpm.c | 33 +++++++++++++++++++++++++++++++++ include/grub/tpm.h | 1 + 3 files changed, 57 insertions(+) diff --git a/grub-core/commands/bli.c b/grub-core/commands/bli.c index 298c5f70a..38f52f87a 100644 --- a/grub-core/commands/bli.c +++ b/grub-core/commands/bli.c @@ -28,6 +28,7 @@ #include #include #include +#include #include GRUB_MOD_LICENSE ("GPLv3+"); @@ -127,12 +128,34 @@ set_loader_device_part_uuid (void) return status; } +static grub_err_t +set_loader_active_pcr_banks (void) +{ + grub_efi_uint32_t active_pcr_banks; + char *active_pcr_banks_str; + grub_err_t status; + + active_pcr_banks = grub_tpm2_active_pcr_banks(); + active_pcr_banks_str = grub_xasprintf ("0x%08x", active_pcr_banks); + if (active_pcr_banks_str == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate active PCR banks string")); + + status = grub_efi_set_variable_to_string ("LoaderTpm2ActivePcrBanks", + &bli_vendor_guid, + active_pcr_banks_str, + GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS | + GRUB_EFI_VARIABLE_RUNTIME_ACCESS); + grub_free (active_pcr_banks_str); + return status; +} + GRUB_MOD_INIT (bli) { grub_efi_set_variable_to_string ("LoaderInfo", &bli_vendor_guid, PACKAGE_STRING, GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS | GRUB_EFI_VARIABLE_RUNTIME_ACCESS); set_loader_device_part_uuid (); + set_loader_active_pcr_banks (); /* No error here is critical, other than being logged */ grub_print_error (); } diff --git a/grub-core/commands/efi/tpm.c b/grub-core/commands/efi/tpm.c index cbac69866..59d0b6708 100644 --- a/grub-core/commands/efi/tpm.c +++ b/grub-core/commands/efi/tpm.c @@ -332,3 +332,36 @@ grub_tpm_present (void) return grub_tpm2_present (tpm); } } + +grub_uint32_t +grub_tpm2_active_pcr_banks (void) +{ + grub_efi_handle_t tpm_handle; + grub_efi_uint8_t protocol_version; + grub_efi_tpm2_protocol_t *tpm; + grub_efi_uint32_t active_pcr_banks = 0; + + if (!grub_tpm_handle_find (&tpm_handle, &protocol_version)) + return 0; + + if (protocol_version == 1) + return 0; /* We report TPM2 status */ + + tpm = grub_efi_open_protocol (tpm_handle, &tpm2_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (tpm == NULL) + { + grub_dprintf ("tpm", "Cannot open TPM2 protocol\n"); + return 0; + } + + if (grub_tpm2_present (tpm)) + { + grub_efi_status_t status = tpm->get_active_pcr_banks (tpm, &active_pcr_banks); + + if (status != GRUB_EFI_SUCCESS) + return 0; /* Assume none available if the call fails. */ + } + + return active_pcr_banks; +} diff --git a/include/grub/tpm.h b/include/grub/tpm.h index d09783dac..fd0956b39 100644 --- a/include/grub/tpm.h +++ b/include/grub/tpm.h @@ -39,6 +39,7 @@ grub_err_t grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, const char *description); int grub_tpm_present (void); +grub_uint32_t grub_tpm2_active_pcr_banks (void); static inline bool grub_is_tpm_fail_fatal (void) From 6831d242adf2e90f3e483c323c0b1d0f080790f5 Mon Sep 17 00:00:00 2001 From: Frediano Ziglio Date: Wed, 16 Jul 2025 09:06:09 +0100 Subject: [PATCH 002/208] loader/efi/linux: Return correct size from LoadFile2 From UEFI specifications 2.10, section 13.2.2, EFI_LOAD_FILE2_PROTOCOL.LoadFile (see https://uefi.org/specs/UEFI/2.10/13_Protocols_Media_Access.html), for BufferSize: On input the size of Buffer in bytes. On output with a return code of EFI_SUCCESS, the amount of data transferred to Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, the size of Buffer required to retrieve the requested file. So, set *buffer_size correctly. Signed-off-by: Frediano Ziglio Reviewed-by: Daniel Kiper --- grub-core/loader/efi/linux.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c index 2c7c874f2..4f5589862 100644 --- a/grub-core/loader/efi/linux.c +++ b/grub-core/loader/efi/linux.c @@ -372,6 +372,8 @@ grub_efi_initrd_load_file2 (grub_efi_load_file2_t *this, if (grub_initrd_load (&initrd_ctx, buffer)) status = GRUB_EFI_DEVICE_ERROR; + else + *buffer_size = initrd_size; grub_initrd_close (&initrd_ctx); return status; From 80df5e13275cc5f090a971cf2b1240f32a99ca2e Mon Sep 17 00:00:00 2001 From: Frediano Ziglio Date: Mon, 21 Jul 2025 17:25:57 +0100 Subject: [PATCH 003/208] loader/arm64/xen_boot: Set correctly bootargs property for modules The cmdline_size already account for NUL terminator, you can see this in xen_boot_binary_load(). The same property is set correctly for Xen command line. Signed-off-by: Frediano Ziglio Reviewed-by: Daniel Kiper --- grub-core/loader/arm64/xen_boot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/loader/arm64/xen_boot.c b/grub-core/loader/arm64/xen_boot.c index 14afec143..ed22b49e0 100644 --- a/grub-core/loader/arm64/xen_boot.c +++ b/grub-core/loader/arm64/xen_boot.c @@ -174,7 +174,7 @@ prepare_xen_module_params (struct xen_boot_binary *module, void *xen_boot_fdt) module->cmdline, module->cmdline, module->cmdline_size); retval = grub_fdt_set_prop (xen_boot_fdt, module_node, "bootargs", - module->cmdline, module->cmdline_size + 1); + module->cmdline, module->cmdline_size); if (retval) return grub_error (GRUB_ERR_IO, "failed to update FDT"); } From eb56a6af9a2a3a367da90ba6cc7bc80b82ba6ba5 Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Wed, 13 Aug 2025 20:36:43 -0500 Subject: [PATCH 004/208] include/xen/xen.h: Rename MAX_GUEST_CMDLINE to GRUB_XEN_MAX_GUEST_CMDLINE The include/xen/xen.h header was using an overly generic name to refer to the maximum length of the command line passed from Xen to a guest. Rename it to avoid confusion or conflicts in the future. Signed-off-by: Aaron Rainbolt Reviewed-by: Daniel Kiper --- include/xen/xen.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/xen/xen.h b/include/xen/xen.h index 692f97a5b..fdf0fc448 100644 --- a/include/xen/xen.h +++ b/include/xen/xen.h @@ -823,8 +823,8 @@ struct start_info { /* (PFN of pre-loaded module if */ /* SIF_MOD_START_PFN set in flags). */ unsigned long mod_len; /* Size (bytes) of pre-loaded module. */ -#define MAX_GUEST_CMDLINE 1024 - int8_t cmd_line[MAX_GUEST_CMDLINE]; +#define GRUB_XEN_MAX_GUEST_CMDLINE 1024 + int8_t cmd_line[GRUB_XEN_MAX_GUEST_CMDLINE]; /* The pfn range here covers both page table and p->m table frames. */ unsigned long first_p2m_pfn;/* 1st pfn forming initial P->M table. */ unsigned long nr_p2m_frames;/* # of pfns forming initial P->M table. */ From eb76b064d207b9d90339176230223850456c62b0 Mon Sep 17 00:00:00 2001 From: Mate Kukri Date: Thu, 24 Jul 2025 10:33:44 +0100 Subject: [PATCH 005/208] build: Add util/import_gcrypt_inth.sed to EXTRA_DISTS This new file was not added to the distribution tarball during the last libgcrypt import. Fixes: 0739d24cd164 (libgcrypt: Adjust import script, definitions and API users for libgcrypt 1.11) Signed-off-by: Mate Kukri Reviewed-by: Daniel Kiper --- conf/Makefile.extra-dist | 1 + 1 file changed, 1 insertion(+) diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index f4e3c5fba..3edbb1872 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -51,6 +51,7 @@ EXTRA_DIST += BUGS EXTRA_DIST += util/i386/efi/grub-dumpdevtree EXTRA_DIST += util/spkmodem-recv.c EXTRA_DIST += util/import_gcrypth.sed +EXTRA_DIST += util/import_gcrypt_inth.sed EXTRA_DIST += util/bin2h.c EXTRA_DIST += util/grub-gen-asciih.c EXTRA_DIST += util/grub-gen-widthspec.c From 29d515b4cc0ff2fabbc6e6ff1d14c2238ad7db45 Mon Sep 17 00:00:00 2001 From: Mate Kukri Date: Thu, 14 Aug 2025 17:12:03 +0200 Subject: [PATCH 006/208] build: Add new libgcrypt and libtasn1 related files to EXTRA_DISTS These files were not added to EXTRA_DISTS during the libgcrypt and libtasn1 imports but are required for autogen.sh to work. Signed-off-by: Mate Kukri Reviewed-by: Daniel Kiper --- conf/Makefile.extra-dist | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index 3edbb1872..49bca5577 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -34,8 +34,33 @@ EXTRA_DIST += grub-core/lib/gnulib-patches/fix-regexec-resource-leak.patch EXTRA_DIST += grub-core/lib/gnulib-patches/fix-gcc-15-compile.patch EXTRA_DIST += grub-core/lib/gnulib-patches/fix-unused-value.patch +EXTRA_DIST += grub-core/lib/libgcrypt-patches/01_md.patch +EXTRA_DIST += grub-core/lib/libgcrypt-patches/02_keccak_sse.patch +EXTRA_DIST += grub-core/lib/libgcrypt-patches/03_mpiutil_alloc.patch +EXTRA_DIST += grub-core/lib/libgcrypt-patches/03_sexp_free.patch +EXTRA_DIST += grub-core/lib/libgcrypt-patches/04_aria.patch +EXTRA_DIST += grub-core/lib/libgcrypt-patches/05_disable_rsa_shake.patch +EXTRA_DIST += grub-core/lib/libgcrypt-patches/06_blake.patch +EXTRA_DIST += grub-core/lib/libgcrypt-patches/07_disable_64div.patch +EXTRA_DIST += grub-core/lib/libgcrypt-patches/08_sexp_leak.patch + +EXTRA_DIST += grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch +EXTRA_DIST += grub-core/lib/libtasn1-patches/0002-libtasn1-replace-strcat-with-strcpy-in-_asn1_str_cat.patch +EXTRA_DIST += grub-core/lib/libtasn1-patches/0003-libtasn1-replace-strcat-with-_asn1_str_cat.patch +EXTRA_DIST += grub-core/lib/libtasn1-patches/0004-libtasn1-adjust-the-header-paths-in-libtasn1.h.patch +EXTRA_DIST += grub-core/lib/libtasn1-patches/0005-libtasn1-Use-grub_divmod64-for-division.patch +EXTRA_DIST += grub-core/lib/libtasn1-patches/0006-libtasn1-fix-the-potential-buffer-overrun.patch +EXTRA_DIST += grub-core/lib/libtasn1-patches/0007-asn1_test-include-asn1_test.h-only.patch +EXTRA_DIST += grub-core/lib/libtasn1-patches/0008-asn1_test-rename-the-main-functions-to-the-test-name.patch +EXTRA_DIST += grub-core/lib/libtasn1-patches/0009-asn1_test-return-either-0-or-1-to-reflect-the-result.patch +EXTRA_DIST += grub-core/lib/libtasn1-patches/0010-asn1_test-remove-verbose-and-the-unnecessary-printf.patch +EXTRA_DIST += grub-core/lib/libtasn1-patches/0011-asn1_test-print-the-error-messages-with-grub_printf.patch +EXTRA_DIST += grub-core/lib/libtasn1-patches/0012-asn1_test-use-the-grub-specific-functions-and-types.patch +EXTRA_DIST += grub-core/lib/libtasn1-patches/0013-asn1_test-enable-the-testcase-only-when-GRUB_LONG_MA.patch + EXTRA_DIST += grub-core/lib/libgcrypt EXTRA_DIST += grub-core/lib/libgcrypt-grub/mpi/generic +EXTRA_DIST += grub-core/lib/libtasn1 EXTRA_DIST += $(shell find $(top_srcdir)/include -name '*.h') EXTRA_DIST += $(shell find $(top_srcdir)/grub-core/lib -name '*.h') EXTRA_DIST += grub-core/efiemu/runtime/config.h From 67a95527b567b03903863ebd52aa9299e330c73f Mon Sep 17 00:00:00 2001 From: Mate Kukri Date: Thu, 24 Jul 2025 18:06:29 +0100 Subject: [PATCH 007/208] configure: Generate tar-ustar tarball instead of tar-v7 Some of our paths are too long for tar-v7 at this point but tar-ustar is supported by essentially everything. So, let's use that. Signed-off-by: Mate Kukri Reviewed-by: Daniel Kiper --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 83e3ddf90..1036638a9 100644 --- a/configure.ac +++ b/configure.ac @@ -53,7 +53,7 @@ save_program_prefix="${program_prefix}" AC_CANONICAL_TARGET program_prefix="${save_program_prefix}" -AM_INIT_AUTOMAKE([1.11]) +AM_INIT_AUTOMAKE([1.11 tar-ustar]) AC_PREREQ(2.64) AC_CONFIG_SRCDIR([include/grub/dl.h]) AC_CONFIG_HEADERS([config-util.h]) From 6898fcf74d134d7220c533e476585370f25bc378 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Mon, 11 Aug 2025 18:02:54 +0000 Subject: [PATCH 008/208] relocator: Switch to own page table while moving chunks We need to avoid clobbering existing table between starting of chunk movers and the moment we install target page table. Generate temporary table for this rather than hoping that we don't clobber existing one. Fixes 64-bit GhostBSD on 64-bit EFI. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/lib/i386/relocator_common_c.c | 106 +++++++++++++++++++++++- grub-core/lib/mips/relocator.c | 6 ++ grub-core/lib/powerpc/relocator.c | 6 ++ grub-core/lib/relocator.c | 5 +- include/grub/relocator_private.h | 2 + 5 files changed, 123 insertions(+), 2 deletions(-) diff --git a/grub-core/lib/i386/relocator_common_c.c b/grub-core/lib/i386/relocator_common_c.c index 7be609b73..c9c5efa72 100644 --- a/grub-core/lib/i386/relocator_common_c.c +++ b/grub-core/lib/i386/relocator_common_c.c @@ -26,6 +26,8 @@ #include #include +#include + extern grub_uint8_t grub_relocator_forward_start; extern grub_uint8_t grub_relocator_forward_end; extern grub_uint8_t grub_relocator_backward_start; @@ -41,20 +43,122 @@ extern grub_size_t grub_relocator_forward_chunk_size; #define RELOCATOR_SIZEOF(x) (&grub_relocator##x##_end - &grub_relocator##x##_start) -grub_size_t grub_relocator_align = 1; grub_size_t grub_relocator_forward_size; grub_size_t grub_relocator_backward_size; +grub_size_t grub_relocator_preamble_size = 0; #ifdef __x86_64__ grub_size_t grub_relocator_jumper_size = 12; #else grub_size_t grub_relocator_jumper_size = 7; #endif +#if defined(__x86_64__) && defined(GRUB_MACHINE_EFI) +grub_size_t grub_relocator_align = 4096; +#else +grub_size_t grub_relocator_align = 1; +#endif + +#if defined(__x86_64__) && defined(GRUB_MACHINE_EFI) + +#define PAGE_PRESENT 1 +#define PAGE_WRITABLE 2 +#define PAGE_USER 4 +#define PAGE_PS 0x80 +#define PAGE_IDX_SIZE 9 +#define PAGE_PS_SHIFT 21 +#define PAGE_NUM_ENTRIES 0x200 +#define PS_PAGE_SIZE 0x200000 + +static grub_uint64_t max_ram_size; + + /* Helper for grub_get_multiboot_mmap_count. */ +static int +max_hook (grub_uint64_t addr, + grub_uint64_t size, + grub_memory_type_t type __attribute__ ((unused)), + void *data __attribute__ ((unused))) +{ + max_ram_size = grub_max (max_ram_size, addr + size); + return 0; +} + +static grub_uint64_t +find_max_size (void) +{ + if (!max_ram_size) + { + /* We need to map the first 4GiB of address space as well as all the + available RAM, so start with 4GiB and increase if we see any RAM + above this. */ + max_ram_size = 1ULL << 32; + + grub_mmap_iterate (max_hook, NULL); + } + + return max_ram_size; +} + +void +grub_cpu_relocator_preamble (void *rels) +{ + grub_uint64_t nentries = (find_max_size () + PS_PAGE_SIZE - 1) >> PAGE_PS_SHIFT; + grub_uint64_t npt2pages = (nentries + PAGE_NUM_ENTRIES - 1) >> PAGE_IDX_SIZE; + grub_uint64_t npt3pages = (npt2pages + PAGE_NUM_ENTRIES - 1) >> PAGE_IDX_SIZE; + grub_uint8_t *p = rels; + grub_uint64_t *pt4 = (grub_uint64_t *) (p + GRUB_PAGE_SIZE); + grub_uint64_t *pt3 = pt4 + PAGE_NUM_ENTRIES; + grub_uint64_t *pt2 = pt3 + (npt3pages << PAGE_IDX_SIZE); + grub_uint64_t *endpreamble = pt2 + (npt2pages << PAGE_IDX_SIZE); + grub_uint64_t i; + + /* movabs $pt4, %rax. */ + *p++ = 0x48; + *p++ = 0xb8; + *(grub_uint64_t *) p = (grub_uint64_t) pt4; + p += 8; + + /* mov %rax, %cr3. */ + *p++ = 0x0f; + *p++ = 0x22; + *p++ = 0xd8; + + /* jmp $endpreamble. */ + *p++ = 0xe9; + *(grub_uint32_t *) p = (grub_uint8_t *) endpreamble - p - 4; + + for (i = 0; i < npt3pages; i++) + pt4[i] = ((grub_uint64_t) pt3 + (i << GRUB_PAGE_SHIFT)) | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER; + + for (i = 0; i < npt2pages; i++) + pt3[i] = ((grub_uint64_t) pt2 + (i << GRUB_PAGE_SHIFT)) | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER; + + for (i = 0; i < (npt2pages << PAGE_IDX_SIZE); i++) + pt2[i] = (i << PAGE_PS_SHIFT) | PAGE_PS | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER; +} + +static void +compute_preamble_size (void) +{ + grub_uint64_t nentries = (find_max_size () + PS_PAGE_SIZE - 1) >> PAGE_PS_SHIFT; + grub_uint64_t npt2pages = (nentries + PAGE_NUM_ENTRIES - 1) >> PAGE_IDX_SIZE; + grub_uint64_t npt3pages = (npt2pages + PAGE_NUM_ENTRIES - 1) >> PAGE_IDX_SIZE; + grub_relocator_preamble_size = (npt2pages + npt3pages + 1 + 1) << GRUB_PAGE_SHIFT; +} + +#else +void +grub_cpu_relocator_preamble (void *rels __attribute__((unused))) +{ +} +#endif void grub_cpu_relocator_init (void) { grub_relocator_forward_size = RELOCATOR_SIZEOF (_forward); grub_relocator_backward_size = RELOCATOR_SIZEOF (_backward); +#if defined(__x86_64__) && defined(GRUB_MACHINE_EFI) + compute_preamble_size (); +#endif } void diff --git a/grub-core/lib/mips/relocator.c b/grub-core/lib/mips/relocator.c index 773f3b769..9f79b40c8 100644 --- a/grub-core/lib/mips/relocator.c +++ b/grub-core/lib/mips/relocator.c @@ -45,6 +45,7 @@ grub_size_t grub_relocator_align = sizeof (grub_uint32_t); grub_size_t grub_relocator_forward_size; grub_size_t grub_relocator_backward_size; grub_size_t grub_relocator_jumper_size = JUMP_SIZEOF + REGW_SIZEOF; +grub_size_t grub_relocator_preamble_size = 0; void grub_cpu_relocator_init (void) @@ -53,6 +54,11 @@ grub_cpu_relocator_init (void) grub_relocator_backward_size = RELOCATOR_SIZEOF(backward); } +void +grub_cpu_relocator_preamble (void *rels __attribute__ ((unused))) +{ +} + static void write_reg (int regn, grub_uint32_t val, void **target) { diff --git a/grub-core/lib/powerpc/relocator.c b/grub-core/lib/powerpc/relocator.c index 15aeb0246..559e04b5a 100644 --- a/grub-core/lib/powerpc/relocator.c +++ b/grub-core/lib/powerpc/relocator.c @@ -43,6 +43,7 @@ grub_size_t grub_relocator_align = sizeof (grub_uint32_t); grub_size_t grub_relocator_forward_size; grub_size_t grub_relocator_backward_size; grub_size_t grub_relocator_jumper_size = JUMP_SIZEOF + REGW_SIZEOF; +grub_size_t grub_relocator_preamble_size = 0; void grub_cpu_relocator_init (void) @@ -51,6 +52,11 @@ grub_cpu_relocator_init (void) grub_relocator_backward_size = RELOCATOR_SIZEOF(backward); } +void +grub_cpu_relocator_preamble (void *rels __attribute__((unused))) +{ +} + static void write_reg (int regn, grub_uint32_t val, void **target) { diff --git a/grub-core/lib/relocator.c b/grub-core/lib/relocator.c index 3306a1bb7..1e1e09704 100644 --- a/grub-core/lib/relocator.c +++ b/grub-core/lib/relocator.c @@ -110,7 +110,7 @@ grub_relocator_new (void) return NULL; ret->postchunks = ~(grub_phys_addr_t) 0; - ret->relocators_size = grub_relocator_jumper_size; + ret->relocators_size = grub_relocator_jumper_size + grub_relocator_preamble_size; grub_dprintf ("relocator", "relocators_size=%lu\n", (unsigned long) ret->relocators_size); return ret; @@ -1605,6 +1605,9 @@ grub_relocator_prepare_relocs (struct grub_relocator *rel, grub_addr_t addr, grub_free (to); } + grub_cpu_relocator_preamble (rels); + rels += grub_relocator_preamble_size; + for (j = 0; j < nchunks; j++) { grub_dprintf ("relocator", "sorted chunk %p->%p, 0x%lx\n", diff --git a/include/grub/relocator_private.h b/include/grub/relocator_private.h index d8e972e01..273add76d 100644 --- a/include/grub/relocator_private.h +++ b/include/grub/relocator_private.h @@ -27,6 +27,7 @@ extern grub_size_t grub_relocator_align; extern grub_size_t grub_relocator_forward_size; extern grub_size_t grub_relocator_backward_size; extern grub_size_t grub_relocator_jumper_size; +extern grub_size_t grub_relocator_preamble_size; void grub_cpu_relocator_init (void); @@ -39,6 +40,7 @@ void grub_cpu_relocator_forward (void *rels, void *src, void *tgt, void grub_cpu_relocator_backward (void *rels, void *src, void *tgt, grub_size_t size); void grub_cpu_relocator_jumper (void *rels, grub_addr_t addr); +void grub_cpu_relocator_preamble (void *rels); /* Remark: GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT_LOG = 1 or 2 aren't supported. */ From 19c698d123ae46d7a8fbf425067aff2d10dac8ca Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Wed, 23 Jul 2025 16:40:04 +0100 Subject: [PATCH 009/208] zfs: Fix LINUX_ROOT_DEVICE when grub-probe fails When grub-probe fails, the current code is to just stuff an empty result in which causes the user to not knowingly have a system that no longer boots. grub-probe can fail because the ZFS pool that contains the root filesystem might have features that GRUB does not yet support which is a common configuration for people with a rpool and a bpool. This behavior uses the zdb utility to dump the same value as the filesystem label would print. Signed-off-by: Doug Goldstein Reviewed-by: Daniel Kiper --- util/grub.d/10_linux.in | 2 +- util/grub.d/20_linux_xen.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index cc393be7e..07649cd3b 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -73,7 +73,7 @@ case x"$GRUB_FS" in GRUB_CMDLINE_LINUX="rootflags=subvol=${rootsubvol} ${GRUB_CMDLINE_LINUX}" fi;; xzfs) - rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || true` + rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || zdb -l ${GRUB_DEVICE} | awk -F \' '/ name/ { print $2 }'` bootfs="`make_system_path_relative_to_its_root / | sed -e "s,@$,,"`" LINUX_ROOT_DEVICE="ZFS=${rpool}${bootfs%/}" ;; diff --git a/util/grub.d/20_linux_xen.in b/util/grub.d/20_linux_xen.in index 94dd8be13..27bff00a8 100644 --- a/util/grub.d/20_linux_xen.in +++ b/util/grub.d/20_linux_xen.in @@ -81,7 +81,7 @@ case x"$GRUB_FS" in GRUB_CMDLINE_LINUX="rootflags=subvol=${rootsubvol} ${GRUB_CMDLINE_LINUX}" fi;; xzfs) - rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || true` + rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || zdb -l ${GRUB_DEVICE} | awk -F \' '/ name/ { print $2 }'` bootfs="`make_system_path_relative_to_its_root / | sed -e "s,@$,,"`" LINUX_ROOT_DEVICE="ZFS=${rpool}${bootfs%/}" ;; From b2a975bc5d516df1c3495d6d80878d65c3199114 Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Wed, 13 Aug 2025 20:36:44 -0500 Subject: [PATCH 010/208] include/xen/xen.h: Add warning comment for cmd_line The cmd_line field of the start_info struct is not guaranteed to be NUL-terminated, even though it is intended to contain a NUL-terminated string. Add a warning about this in a comment so future consumers of this field know to check it for a NUL terminator before using it. Signed-off-by: Aaron Rainbolt Reviewed-by: Daniel Kiper --- include/xen/xen.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/xen/xen.h b/include/xen/xen.h index fdf0fc448..4c21fb5ef 100644 --- a/include/xen/xen.h +++ b/include/xen/xen.h @@ -823,6 +823,11 @@ struct start_info { /* (PFN of pre-loaded module if */ /* SIF_MOD_START_PFN set in flags). */ unsigned long mod_len; /* Size (bytes) of pre-loaded module. */ + /* + * cmd_line will contain a NUL-termianted string if it contains valid + * data, but it MAY be invalid and not contain a NUL byte at all. Code + * that accesses cmd_line MUST NOT assume it is NUL-terminated. + */ #define GRUB_XEN_MAX_GUEST_CMDLINE 1024 int8_t cmd_line[GRUB_XEN_MAX_GUEST_CMDLINE]; /* The pfn range here covers both page table and p->m table frames. */ From 587db89afc959f83c552bcbb96cd679cbdc2991a Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Wed, 13 Aug 2025 20:36:45 -0500 Subject: [PATCH 011/208] kern/xen: Add Xen command line parsing Xen traditionally allows customizing guest behavior by passing arguments to the VM kernel via the kernel command line. This is no longer possible when using GRUB with Xen, as the kernel command line is decided by the GRUB configuration file within the guest, not data passed to the guest by Xen. To work around this limitation, enable GRUB to parse a command line passed to it by Xen, and expose data from the command line to the GRUB configuration as environment variables. These variables can be used in the GRUB configuration for any desired purpose, such as extending the kernel command line passed to the guest. The command line format is inspired by the Linux kernel's command line format. To reduce the risk of misuse, abuse, or accidents in production, the command line will only be parsed if it consists entirely of 7-bit ASCII characters, only alphabetical characters and underscores are permitted in variable names, and all variable names must start with the string "xen_grub_env_". This also allows room for expanding the command line arguments accepted by GRUB in the future, should other arguments end up becoming desirable in the future. Signed-off-by: Aaron Rainbolt Reviewed-by: Daniel Kiper --- docs/grub.texi | 52 +++++ grub-core/Makefile.core.def | 2 + grub-core/kern/i386/xen/pvh.c | 4 + grub-core/kern/xen/cmdline.c | 360 ++++++++++++++++++++++++++++++++++ grub-core/kern/xen/init.c | 2 + include/grub/xen.h | 2 + 6 files changed, 422 insertions(+) create mode 100644 grub-core/kern/xen/cmdline.c diff --git a/docs/grub.texi b/docs/grub.texi index 34b3484dc..cd4e53928 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -3271,6 +3271,7 @@ GRUB. Others may be used freely in GRUB configuration files. @menu * Special environment variables:: * Environment block:: +* Passing environment variables through Xen:: @end menu @@ -3871,6 +3872,57 @@ using BIOS or EFI functions (no ATA, USB or IEEE1275). @command{grub-mkconfig} uses this facility to implement @samp{GRUB_SAVEDEFAULT} (@pxref{Simple configuration}). +@node Passing environment variables through Xen +@section Passing environment variables through Xen + +If you are using a GRUB image as the kernel for a PV or PVH Xen virtual +machine, you can pass environment variables from Xen's dom0 to the VM through +the Xen-provided kernel command line. When combined with a properly configured +guest, this can be used to customize the guest's behavior on bootup via the +VM's Xen configuration file. + +GRUB will parse the kernel command line passed to it by Xen during bootup. +The command line will be split into space-delimited words. Single and +double quotes may be used to quote words or portions of words that contain +spaces. Single quotes will be considered part of a word if inside double +quotes, and vice versa. Arbitrary characters may be backslash-escaped to make +them a literal component of a word rather than being parsed as quotes or word +separators. The command line must consist entirely of printable 7-bit ASCII +characters and spaces. If a non-printing ASCII character is found anywhere in +the command line, the entire command line will be ignored by GRUB. (This +splitter algorithm is meant to behave somewhat like Bash's word splitting.) + +Each word should be a variable assignment in the format ``variable'' or +``variable=value''. Variable names must contain only the characters A-Z, a-z, +and underscore (``_''). Variable names must begin with the string +``xen_grub_env_''. Variable values can contain arbitrary printable 7-bit +ASCII characters and space. If any variable contains an illegal name, that +variable will be ignored. + +If a variable name and value are both specified, the variable will be set to +the specified value. If only a variable name is specified, the variable's +value will be set to ``1''. + +The following is a simple example of how to use this functionality to append +arbitrary variables to a guest's kernel command line: + +@example +# In the Xen configuration file for the guest +name = "linux_vm" +type = "pvh" +kernel = "/path/to/grub-i386-xen_pvh.bin" +extra = "xen_grub_env_linux_append='loglevel=3'" +memory = 1024 +disk = [ "file:/srv/vms/linux_vm.img,sda,w" ] + +# In the guest's GRUB configuration file +menuentry "Linux VM with dom0-specified kernel parameters" @{ + search --set=root --label linux_vm --hint hd0,msdos1 + linux /boot/vmlinuz root=LABEL=linux_vm $@{xen_grub_env_linux_append@} + initrd /boot/initrd.img +@} +@end example + @node Modules @chapter Modules diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index b3f71196a..df0f2666b 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -248,6 +248,7 @@ kernel = { xen = term/xen/console.c; xen = disk/xen/xendisk.c; xen = commands/boot.c; + xen = kern/xen/cmdline.c; i386_xen_pvh = commands/boot.c; i386_xen_pvh = disk/xen/xendisk.c; @@ -255,6 +256,7 @@ kernel = { i386_xen_pvh = kern/i386/xen/tsc.c; i386_xen_pvh = kern/i386/xen/pvh.c; i386_xen_pvh = kern/xen/init.c; + i386_xen_pvh = kern/xen/cmdline.c; i386_xen_pvh = term/xen/console.c; ia64_efi = kern/ia64/efi/startup.S; diff --git a/grub-core/kern/i386/xen/pvh.c b/grub-core/kern/i386/xen/pvh.c index 91fbca859..293b615e8 100644 --- a/grub-core/kern/i386/xen/pvh.c +++ b/grub-core/kern/i386/xen/pvh.c @@ -352,6 +352,10 @@ grub_xen_setup_pvh (void) grub_xen_mm_init_regions (); grub_rsdp_addr = pvh_start_info->rsdp_paddr; + + grub_strncpy ((char *) grub_xen_start_page_addr->cmd_line, + (const char *)(grub_addr_t) pvh_start_info->cmdline_paddr, + GRUB_XEN_MAX_GUEST_CMDLINE); } grub_err_t diff --git a/grub-core/kern/xen/cmdline.c b/grub-core/kern/xen/cmdline.c new file mode 100644 index 000000000..bdea89ecc --- /dev/null +++ b/grub-core/kern/xen/cmdline.c @@ -0,0 +1,360 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2025 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include + +enum splitter_state +{ + SPLITTER_NORMAL = 0x0, + SPLITTER_HIT_BACKSLASH = 0x1, + SPLITTER_IN_SINGLE_QUOTES = 0x2, + SPLITTER_IN_DOUBLE_QUOTES = 0x4, +}; +typedef enum splitter_state splitter_state_t; + +/* + * The initial size of the current_word buffer. The buffer may be resized as + * needed. + */ +#define PARSER_BASE_WORD_SIZE 32 + +struct parser_state +{ + char **words; + grub_size_t words_count; + char *current_word; + grub_size_t current_word_len; + grub_size_t current_word_pos; +}; +typedef struct parser_state parser_state_t; + +static grub_err_t +append_char_to_word (parser_state_t *ps, char c, bool allow_null) +{ + /* + * We ban any chars that are not in the ASCII printable range. If + * allow_null == true, we make an exception for NUL. (This is needed so that + * append_word_to_list can add a NUL terminator to the word). + */ + if (!grub_isprint (c) && allow_null == false) + return GRUB_ERR_BAD_ARGUMENT; + else if (allow_null == true && c != '\0') + return GRUB_ERR_BAD_ARGUMENT; + + if (ps->current_word_pos == ps->current_word_len) + { + ps->current_word = grub_realloc (ps->current_word, ps->current_word_len *= 2); + if (ps->current_word == NULL) + { + ps->current_word_len /= 2; + return grub_errno; + } + } + + ps->current_word[ps->current_word_pos++] = c; + return GRUB_ERR_NONE; +} + +static grub_err_t +append_word_to_list (parser_state_t *ps) +{ + /* No-op on empty words. */ + if (ps->current_word_pos == 0) + return GRUB_ERR_NONE; + + if (append_char_to_word (ps, '\0', true) != GRUB_ERR_NONE) + grub_fatal ("couldn't append NUL terminator to word during Xen cmdline parsing"); + + ps->current_word_len = grub_strlen (ps->current_word) + 1; + ps->current_word = grub_realloc (ps->current_word, ps->current_word_len); + if (ps->current_word == NULL) + return grub_errno; + ps->words = grub_realloc (ps->words, ++ps->words_count * sizeof (char *)); + if (ps->words == NULL) + return grub_errno; + ps->words[ps->words_count - 1] = ps->current_word; + + ps->current_word_len = PARSER_BASE_WORD_SIZE; + ps->current_word_pos = 0; + ps->current_word = grub_malloc (ps->current_word_len); + if (ps->current_word == NULL) + return grub_errno; + + return GRUB_ERR_NONE; +} + +static bool +is_key_safe (char *key, grub_size_t len) +{ + grub_size_t i; + + for (i = 0; i < len; i++) + if (!grub_isalpha (key[i]) && key[i] != '_') + return false; + + return true; +} + +void +grub_parse_xen_cmdline (void) +{ + parser_state_t ps = {0}; + splitter_state_t ss = SPLITTER_NORMAL; + + const char *cmdline = (const char *) grub_xen_start_page_addr->cmd_line; + grub_size_t cmdline_len; + bool cmdline_valid = false; + char **param_keys = NULL; + char **param_vals = NULL; + grub_size_t param_dict_len = 0; + grub_size_t param_dict_pos = 0; + char current_char = '\0'; + grub_size_t i = 0; + + /* + * The following algorithm is used to parse the Xen command line: + * + * - The command line is split into space-separated words. + * - Single and double quotes may be used to suppress the splitting + * behavior of spaces. + * - Double quotes are appended to the current word verbatim if they + * appear within a single-quoted string portion, and vice versa. + * - Backslashes may be used to cause the next character to be + * appended to the current word verbatim. This is only useful when + * used to escape quotes, spaces, and backslashes, but for simplicity + * we allow backslash-escaping anything. + * - After splitting the command line into words, each word is checked to + * see if it contains an equals sign. + * - If it does, it is split on the equals sign into a key-value pair. The + * key is then treated as an variable name, and the value is treated as + * the variable's value. + * - If it does not, the entire word is treated as a variable name. The + * variable's value is implicitly considered to be `1`. + * - All variables detected on the command line are checked to see if their + * names begin with the string `xen_grub_env_`. Variables that do not pass + * this check are discarded, variables that do pass this check are + * exported so they are available to the GRUB configuration. + * + * This behavior is intended to somewhat mimic the splitter behavior in Bash + * and in GRUB's config file parser. + */ + + ps.current_word_len = PARSER_BASE_WORD_SIZE; + ps.current_word = grub_malloc (ps.current_word_len); + if (ps.current_word == NULL) + goto cleanup_main; + + for (i = 0; i < GRUB_XEN_MAX_GUEST_CMDLINE; i++) + { + if (cmdline[i] == '\0') + { + cmdline_valid = true; + break; + } + } + + if (cmdline_valid == false) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, + "command line from Xen is not NUL-terminated"); + grub_print_error (); + goto cleanup_main; + } + + cmdline_len = grub_strlen (cmdline); + for (i = 0; i < cmdline_len; i++) + { + current_char = cmdline[i]; + + /* + * If the previous character was a backslash, append the current + * character to the word verbatim + */ + if (ss & SPLITTER_HIT_BACKSLASH) + { + ss &= ~SPLITTER_HIT_BACKSLASH; + if (append_char_to_word (&ps, current_char, false) != GRUB_ERR_NONE) + goto cleanup_main; + continue; + } + + switch (current_char) + { + case '\\': + /* Backslashes escape arbitrary characters. */ + ss |= SPLITTER_HIT_BACKSLASH; + break; + + case '\'': + /* + * Single quotes suppress word splitting and double quoting until + * the next single quote is encountered. + */ + if (ss & SPLITTER_IN_DOUBLE_QUOTES) + { + if (append_char_to_word (&ps, current_char, false) != GRUB_ERR_NONE) + goto cleanup_main; + break; + } + + ss ^= SPLITTER_IN_SINGLE_QUOTES; + break; + + case '"': + /* + * Double quotes suppress word splitting and single quoting until + * the next double quote is encountered. + */ + if (ss & SPLITTER_IN_SINGLE_QUOTES) + { + if (append_char_to_word (&ps, current_char, false) != GRUB_ERR_NONE) + goto cleanup_main; + break; + } + + ss ^= SPLITTER_IN_DOUBLE_QUOTES; + break; + + case ' ': + /* Spaces separate words in the command line from each other. */ + if (ss & SPLITTER_IN_SINGLE_QUOTES || + ss & SPLITTER_IN_DOUBLE_QUOTES) + { + if (append_char_to_word (&ps, current_char, false) != GRUB_ERR_NONE) + goto cleanup_main; + break; + } + + if (append_word_to_list (&ps) != GRUB_ERR_NONE) + goto cleanup_main; + break; + + default: + if (append_char_to_word (&ps, current_char, false) != GRUB_ERR_NONE) + goto cleanup_main; + } + } + + if (append_word_to_list (&ps) != GRUB_ERR_NONE) + goto cleanup_main; + + param_keys = grub_malloc (ps.words_count * sizeof (char *)); + if (param_keys == NULL) + goto cleanup_main; + param_vals = grub_malloc (ps.words_count * sizeof (char *)); + if (param_vals == NULL) + goto cleanup_main; + + for (i = 0; i < ps.words_count; i++) + { + char *eq_pos; + + ps.current_word = ps.words[i]; + ps.current_word_len = grub_strlen (ps.current_word) + 1; + eq_pos = grub_strchr (ps.current_word, '='); + + if (eq_pos != NULL) + { + /* + * Both pre_eq_len and post_eq_len represent substring lengths + * without a NUL terminator. + */ + grub_size_t pre_eq_len = (grub_size_t) (eq_pos - ps.current_word); + /* + * ps.current_word_len includes the NUL terminator, so we subtract + * one to get rid of the terminator, and one more to get rid of the + * equals sign. + */ + grub_size_t post_eq_len = (ps.current_word_len - 2) - pre_eq_len; + + if (is_key_safe (ps.current_word, pre_eq_len) == true) + { + param_dict_pos = param_dict_len++; + param_keys[param_dict_pos] = grub_malloc (pre_eq_len + 1); + if (param_keys == NULL) + goto cleanup_main; + param_vals[param_dict_pos] = grub_malloc (post_eq_len + 1); + if (param_vals == NULL) + goto cleanup_main; + + grub_strncpy (param_keys[param_dict_pos], ps.current_word, pre_eq_len); + grub_strncpy (param_vals[param_dict_pos], + ps.current_word + pre_eq_len + 1, post_eq_len); + param_keys[param_dict_pos][pre_eq_len] = '\0'; + param_vals[param_dict_pos][post_eq_len] = '\0'; + } + } + else if (is_key_safe (ps.current_word, ps.current_word_len - 1) == true) + { + param_dict_pos = param_dict_len++; + param_keys[param_dict_pos] = grub_malloc (ps.current_word_len); + if (param_keys == NULL) + goto cleanup_main; + param_vals[param_dict_pos] = grub_zalloc (2); + if (param_vals == NULL) + goto cleanup_main; + + grub_strncpy (param_keys[param_dict_pos], ps.current_word, + ps.current_word_len); + if (param_keys[param_dict_pos][ps.current_word_len - 1] != '\0' ) + grub_fatal ("NUL terminator missing from key during Xen cmdline parsing"); + *param_vals[param_dict_pos] = '1'; + } + } + + for (i = 0; i < param_dict_len; i++) + { + /* + * Find keys that start with "xen_grub_env_" and export them + * as environment variables. + */ + if (grub_strncmp (param_keys[i], + "xen_grub_env_", + sizeof ("xen_grub_env_") - 1) != 0) + continue; + + if (grub_env_set (param_keys[i], param_vals[i]) != GRUB_ERR_NONE) + { + grub_printf ("warning: could not set environment variable `%s' to value `%s'\n", + param_keys[i], param_vals[i]); + continue; + } + + if (grub_env_export (param_keys[i]) != GRUB_ERR_NONE) + grub_printf ("warning: could not export environment variable `%s'", + param_keys[i]); + } + + cleanup_main: + for (i = 0; i < ps.words_count; i++) + grub_free (ps.words[i]); + + for (i = 0; i < param_dict_len; i++) + { + grub_free (param_keys[i]); + grub_free (param_vals[i]); + } + + grub_free (param_keys); + grub_free (param_vals); + grub_free (ps.words); +} diff --git a/grub-core/kern/xen/init.c b/grub-core/kern/xen/init.c index 782ca7295..69cf59f0c 100644 --- a/grub-core/kern/xen/init.c +++ b/grub-core/kern/xen/init.c @@ -581,6 +581,8 @@ grub_machine_init (void) grub_xendisk_init (); grub_boot_init (); + + grub_parse_xen_cmdline (); } void diff --git a/include/grub/xen.h b/include/grub/xen.h index 91cb7cf81..7f9efee80 100644 --- a/include/grub/xen.h +++ b/include/grub/xen.h @@ -89,6 +89,8 @@ void grub_console_init (void); void grub_xendisk_fini (void); void grub_xendisk_init (void); +void grub_parse_xen_cmdline (void); + #ifdef __x86_64__ typedef grub_uint64_t grub_xen_mfn_t; #else From e016d6d60a139045307122a03f59e002d5892b89 Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Tue, 12 Aug 2025 03:45:32 +0000 Subject: [PATCH 012/208] kern/misc: Implement grub_strtok() Add the functions grub_strtok() and grub_strtok_r() to help parse strings into tokens separated by characters in the "delim" parameter. These functions are present in gnulib but calling them directly from the gnulib code is quite challenging since the call "#include " would include the header file grub-core/lib/posix_wrap/string.h instead of grub-core/lib/gnulib/string.h, where strtok() and strtok_r() are declared. Since this overlap is quite problematic, the simpler solution was to implement the code in the GRUB based on gnulib's implementation. For more information on these functions, visit the Linux Programmer's Manual, man strtok. Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/kern/misc.c | 62 +++++++++++++++++++++++++++++++++++++++++++ include/grub/misc.h | 3 +++ 2 files changed, 65 insertions(+) diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index 2b7922393..258f91893 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -401,6 +401,68 @@ grub_strword (const char *haystack, const char *needle) return 0; } +char * +grub_strtok_r (char *s, const char *delim, char **save_ptr) +{ + char *token; + const char *c; + bool is_delim; + + if (s == NULL) + s = *save_ptr; + + /* Scan leading delimiters. */ + while (*s != '\0') + { + is_delim = false; + for (c = delim; *c != '\0'; c++) + { + if (*s == *c) + { + is_delim = true; + break; + } + } + if (is_delim == true) + s++; + else + break; + } + + if (*s == '\0') + { + *save_ptr = s; + return NULL; + } + + /* Find the end of the token. */ + token = s; + while (*s != '\0') + { + for (c = delim; *c != '\0'; c++) + { + if (*s == *c) + { + *s = '\0'; + *save_ptr = s + 1; + return token; + } + } + s++; + } + + *save_ptr = s; + return token; +} + +char * +grub_strtok (char *s, const char *delim) +{ + static char *last; + + return grub_strtok_r (s, delim, &last); +} + int grub_isspace (int c) { diff --git a/include/grub/misc.h b/include/grub/misc.h index e087e7b3e..9522d7305 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -126,6 +126,9 @@ char *EXPORT_FUNC(grub_strchr) (const char *s, int c); char *EXPORT_FUNC(grub_strrchr) (const char *s, int c); int EXPORT_FUNC(grub_strword) (const char *s, const char *w); +char *EXPORT_FUNC(grub_strtok_r) (char *s, const char *delim, char **save_ptr); +char *EXPORT_FUNC(grub_strtok) (char *s, const char *delim); + /* Copied from gnulib. Written by Bruno Haible , 2005. */ static inline char * From 8cee1c284bb526c04785bcd169228bade2b1b2e0 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 12 Aug 2025 03:45:33 +0000 Subject: [PATCH 013/208] blsuki: Add blscfg command to parse Boot Loader Specification snippets The BootLoaderSpec (BLS) defines a scheme where different bootloaders can share a format for boot items and a configuration directory that accepts these common configurations as drop-in files. The BLS Specification: https://uapi-group.org/specifications/specs/boot_loader_specification/ Signed-off-by: Peter Jones Signed-off-by: Javier Martinez Canillas Signed-off-by: Will Thompson Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- bootstrap.conf | 1 + docs/grub.texi | 75 +++ grub-core/Makefile.core.def | 12 + grub-core/commands/blsuki.c | 1028 ++++++++++++++++++++++++++++++++ grub-core/commands/legacycfg.c | 4 +- grub-core/commands/menuentry.c | 8 +- grub-core/normal/main.c | 6 + include/grub/menu.h | 15 + include/grub/normal.h | 2 +- 9 files changed, 1145 insertions(+), 6 deletions(-) create mode 100644 grub-core/commands/blsuki.c diff --git a/bootstrap.conf b/bootstrap.conf index 3590aba99..8eff35b45 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -24,6 +24,7 @@ gnulib_modules=" argp base64 error + filevercmp fnmatch getdelim getline diff --git a/docs/grub.texi b/docs/grub.texi index cd4e53928..e838177cd 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -3282,6 +3282,7 @@ These variables have special meaning to GRUB. @menu * biosnum:: +* blsuki_save_default:: * check_signatures:: * chosen:: * cmdpath:: @@ -3345,6 +3346,13 @@ For an alternative approach which also changes BIOS drive mappings for the chain-loaded system, @pxref{drivemap}. +@node blsuki_save_default +@subsection blsuki_save_default + +If this variable is set, menu entries generated from BLS config files +(@pxref{blscfg}) will be set as the default boot entry when selected. + + @node check_signatures @subsection check_signatures @@ -6471,6 +6479,7 @@ you forget a command, you can run the command @command{help} * background_image:: Load background image for active terminal * badram:: Filter out bad regions of RAM * blocklist:: Print a block list +* blscfg:: Load Boot Loader Specification menu entries * boot:: Start up your operating system * cat:: Show the contents of a file * clear:: Clear the screen @@ -6659,6 +6668,72 @@ Print a block list (@pxref{Block list syntax}) for @var{file}. @end deffn +@node blscfg +@subsection blscfg + +@deffn Command blscfg [@option{-p|--path} dir] [@option{-f|--enable-fallback}] [@option{-d|--show-default}] [@option{-n|--show-non-default}] [@option{-e|--entry} file] +Load Boot Loader Specification (BLS) entries into the GRUB menu. Boot entries +generated from @command{blscfg} won't interfere with entries from @file{grub.cfg} appearing in +the GRUB menu. Also, entries generated from @command{blscfg} exists only in memory and +don't update @file{grub.cfg}. + +By default, the BLS entries are stored in the @file{/loader/entries} directory in the +boot partition. If BLS entries are stored elsewhere, the @option{--path} option can be +used to check a different directory instead of the default location. If no BLS +entries are found while using the @option{--path} option, the @option{--enable-fallback} option +can be used to check for entries in the default location. + +The @option{--show-default} option allows the default boot entry to be added to the +GRUB menu from the BLS entries. + +The @option{--show-non-default} option allows non-default boot entries to be added to +the GRUB menu from the BLS entries. + +The @option{--entry} option allows specific boot entries to be added to the GRUB menu +from the BLS entries. + +The @option{--entry}, @option{--show-default}, and @option{--show-non-default} options +are used to filter which BLS entries are added to the GRUB menu. If none are +used, all entries in the default location or the location specified by @option{--path} +will be added to the GRUB menu. + +A BLS config file example: +@example +# /boot/loader/entries/6a9857a393724b7a981ebb5b8495b9ea-3.8.0-2.fc19.x86_64.conf +title Fedora 19 (Rawhide) +sort-key fedora +machine-id 6a9857a393724b7a981ebb5b8495b9ea +version 3.8.0-2.fc19.x86_64 +options root=UUID=6d3376e4-fc93-4509-95ec-a21d68011da2 quiet +architecture x64 +linux /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux +initrd /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/initrd +@end example + +For more information on BLS entry keys as well as other information on BLS, +see: @uref{https://uapi-group.org/specifications/specs/boot_loader_specification/, The Boot Loader Specification}. For the GRUB, there are a few additional +BLS entry keys based on the @command{menuentry} command (@pxref{menuentry}). + +The @code{grub_class} key may be used any number of times to group menu entries into +classes. Menu themes may display different classes using different styles. + +The @code{grub_users} key grants specific users access to specific menu +entries. @xref{Security}. + +The @code{grub_hotkey} key associates a hotkey with a menu entry. +@var{key} may be a single letter, or one of the aliases @samp{backspace}, +@samp{tab}, or @samp{delete}. + +The @code{grub_args} key can be used for any other argument to be passed as positonal +parameters when the list of commands generated from the BLS config file are +executed. + +Variable expansion using the @samp{$} character (@xref{Shell-like scripting}) may be +used with BLS config files for the GRUB but might not be compatible with other +bootloaders. +@end deffn + + @node boot @subsection boot diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index df0f2666b..6bd5f2c28 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -847,6 +847,18 @@ module = { common = commands/blocklist.c; }; +module = { + name = blsuki; + common = commands/blsuki.c; + common = lib/gnulib/filevercmp.c; + enable = powerpc_ieee1275; + enable = efi; + enable = i386_pc; + enable = emu; + cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)'; + cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)'; +}; + module = { name = boot; common = commands/boot.c; diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c new file mode 100644 index 000000000..9440adb10 --- /dev/null +++ b/grub-core/commands/blsuki.c @@ -0,0 +1,1028 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2025 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define GRUB_BLS_CONFIG_PATH "/loader/entries/" + +#define BLS_EXT_LEN (sizeof (".conf") - 1) + +/* + * It is highly unlikely to ever receive a large amount of keyval pairs. A + * limit of 10000 is more than enough. + */ +#define BLSUKI_KEYVALS_MAX 10000 + +static const struct grub_arg_option bls_opt[] = + { + {"path", 'p', 0, "Specify path to find BLS entries.", N_("DIR"), ARG_TYPE_PATHNAME}, + {"enable-fallback", 'f', 0, "Fallback to the default BLS path if --path fails to find BLS entries.", 0, ARG_TYPE_NONE}, + {"show-default", 'd', 0, "Allow the default BLS entry to be added to the GRUB menu.", 0, ARG_TYPE_NONE}, + {"show-non-default", 'n', 0, "Allow the non-default BLS entries to be added to the GRUB menu.", 0, ARG_TYPE_NONE}, + {"entry", 'e', 0, "Allow specific BLS entries to be added to the GRUB menu.", N_("FILE"), ARG_TYPE_FILE}, + {0, 0, 0, 0, 0, 0} + }; + +struct keyval +{ + const char *key; + char *val; +}; + +struct read_entry_info +{ + const char *devid; + const char *dirname; + grub_file_t file; +}; + +struct find_entry_info +{ + const char *dirname; + const char *devid; + grub_device_t dev; + grub_fs_t fs; +}; + +static grub_blsuki_entry_t *entries; + +#define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries) + +/* + * This function will add a new keyval pair to a list of keyvals stored in the + * entry parameter. + */ +static grub_err_t +blsuki_add_keyval (grub_blsuki_entry_t *entry, char *key, char *val) +{ + char *k, *v; + struct keyval **kvs, *kv; + grub_size_t size; + int new_n = entry->nkeyvals + 1; + + if (new_n > BLSUKI_KEYVALS_MAX) + return grub_error (GRUB_ERR_BAD_NUMBER, "too many keyval pairs"); + + if (entry->keyvals_size == 0) + { + size = sizeof (struct keyval *); + kvs = grub_malloc (size); + if (kvs == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate space for BLS key values"); + + entry->keyvals = kvs; + entry->keyvals_size = size; + } + else if (entry->keyvals_size < new_n * sizeof (struct keyval *)) + { + size = entry->keyvals_size * 2; + kvs = grub_realloc (entry->keyvals, size); + if (kvs == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't reallocate space for BLS key values"); + + entry->keyvals = kvs; + entry->keyvals_size = size; + } + + kv = grub_malloc (sizeof (struct keyval)); + if (kv == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value"); + + k = grub_strdup (key); + if (k == NULL) + { + grub_free (kv); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value"); + } + + v = grub_strdup (val); + if (v == NULL) + { + grub_free (k); + grub_free (kv); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value"); + } + + kv->key = k; + kv->val = v; + entry->keyvals[entry->nkeyvals] = kv; + entry->nkeyvals = new_n; + + return GRUB_ERR_NONE; +} + +/* + * Find the value of the key named by keyname. If there are allowed to be + * more than one, pass a pointer set to -1 to the last parameter the first + * time, and pass the same pointer through each time after, and it'll return + * them in sorted order as defined in the BLS fragment file. + */ +static char * +blsuki_get_val (grub_blsuki_entry_t *entry, const char *keyname, int *last) +{ + int idx, start = (last != NULL) ? (*last + 1) : 0; + struct keyval *kv = NULL; + char *ret = NULL; + + for (idx = start; idx < entry->nkeyvals; idx++) + { + kv = entry->keyvals[idx]; + + if (grub_strcmp (keyname, kv->key) == 0) + { + ret = kv->val; + break; + } + } + + if (last != NULL) + { + if (idx == entry->nkeyvals) + *last = -1; + else + *last = idx; + } + + return ret; +} + +/* + * Add a new grub_blsuki_entry_t struct to the entries list and sort it's + * position on the list. + */ +static grub_err_t +blsuki_add_entry (grub_blsuki_entry_t *entry) +{ + grub_blsuki_entry_t *e, *last = NULL; + int rc; + + if (entries == NULL) + { + grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename); + entries = entry; + return GRUB_ERR_NONE; + } + + FOR_BLSUKI_ENTRIES (e) + { + rc = filevercmp (entry->filename, e->filename); + if (rc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("duplicate file: `%s'"), entry->filename); + + if (rc > 0) + { + grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename); + grub_list_push (GRUB_AS_LIST_P (&e), GRUB_AS_LIST (entry)); + if (entry->next == entries) + { + entries = entry; + entry->prev = NULL; + } + else if (last != NULL) + last->next = entry; + + return GRUB_ERR_NONE; + } + last = e; + } + + if (last != NULL) + { + grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename); + last->next = entry; + entry->prev = &last; + } + + return GRUB_ERR_NONE; +} + +/* + * This function parses each line of a BLS config file to obtain the key value + * pairs that will be used to setup the GRUB menu entries. The key value pair + * will be stored in a list in the entry parameter. + */ +static grub_err_t +bls_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry) +{ + grub_err_t err = GRUB_ERR_NONE; + + for (;;) + { + char *line, *key, *val; + + line = grub_file_getline (f); + if (line == NULL) + break; + + key = grub_strtok_r (line, " \t", &val); + if (key == NULL) + { + grub_free (line); + break; + } + if (*key == '#') + { + grub_free (line); + continue; + } + + while (*val == ' ' || *val == '\t') + val++; + + if (*val == '\0') + { + grub_free (line); + break; + } + + err = blsuki_add_keyval (entry, key, val); + grub_free (line); + if (err != GRUB_ERR_NONE) + break; + } + + return err; +} + +/* + * If a file hasn't already been opened, this function opens a BLS config file + * and initializes entry data before parsing keyvals and adding the entry to + * the list of BLS entries. + */ +static int +blsuki_read_entry (const char *filename, + const struct grub_dirhook_info *dirhook_info __attribute__ ((__unused__)), + void *data) +{ + grub_size_t path_len = 0, filename_len; + grub_err_t err; + char *p = NULL; + grub_file_t f = NULL; + grub_blsuki_entry_t *entry; + struct read_entry_info *info = (struct read_entry_info *) data; + + grub_dprintf ("blsuki", "filename: \"%s\"\n", filename); + + filename_len = grub_strlen (filename); + + if (info->file != NULL) + f = info->file; + else + { + if (filename_len < BLS_EXT_LEN || + grub_strcmp (filename + filename_len - BLS_EXT_LEN, ".conf") != 0) + return 0; + + p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename); + + f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG); + grub_free (p); + if (f == NULL) + goto finish; + } + + entry = grub_zalloc (sizeof (*entry)); + if (entry == NULL) + goto finish; + + /* + * If a file is opened before this function, the filename may have a path. + * Since the filename is used for the ID of the GRUB menu entry, we can + * remove the path. + */ + if (info->file != NULL) + { + char *slash; + + slash = grub_strrchr (filename, '/'); + if (slash != NULL) + path_len = slash - filename + 1; + } + filename_len -= path_len; + + entry->filename = grub_strndup (filename + path_len, filename_len); + if (entry->filename == NULL) + { + grub_free (entry); + goto finish; + } + + err = bls_parse_keyvals (f, entry); + + if (err == GRUB_ERR_NONE) + blsuki_add_entry (entry); + else + grub_free (entry); + + finish: + if (f != NULL) + grub_file_close (f); + + return 0; +} + +/* + * This function returns a list of values that had the same key in the BLS + * config file. The number of entries in this list is returned by the len + * parameter. + */ +static char ** +blsuki_make_list (grub_blsuki_entry_t *entry, const char *key, int *len) +{ + int last = -1; + char *val; + int nlist = 0; + char **list; + + list = grub_zalloc (sizeof (char *)); + if (list == NULL) + return NULL; + + while (1) + { + char **new; + + /* + * Since the same key might appear more than once, the 'last' variable + * starts at -1 and increments to indicate the last index in the list + * we obtained from blsuki_get_val(). + */ + val = blsuki_get_val (entry, key, &last); + if (val == NULL) + break; + + new = grub_realloc (list, (nlist + 2) * sizeof (char *)); + if (new == NULL) + break; + + list = new; + list[nlist++] = val; + list[nlist] = NULL; + } + + if (nlist == 0) + { + grub_free (list); + return NULL; + } + + if (len != NULL) + *len = nlist; + + return list; +} + +/* + * This function appends a field to the end of a buffer. If the field given is + * an enviornmental variable, it gets the value stored for that variable and + * appends that to the buffer instead. + */ +static char * +blsuki_field_append (bool is_env_var, char *buffer, const char *start, const char *end) +{ + char *tmp; + const char *field; + grub_size_t size = 0; + + tmp = grub_strndup (start, end - start + 1); + if (tmp == NULL) + return NULL; + + field = tmp; + + if (is_env_var == true) + { + field = grub_env_get (tmp); + if (field == NULL) + return buffer; + } + + if (grub_add (grub_strlen (field), 1, &size)) + return NULL; + + if (buffer == NULL) + buffer = grub_zalloc (size); + else + { + if (grub_add (size, grub_strlen (buffer), &size)) + return NULL; + + buffer = grub_realloc (buffer, size); + } + + if (buffer == NULL) + return NULL; + + tmp = buffer + grub_strlen (buffer); + tmp = grub_stpcpy (tmp, field); + + if (is_env_var == true) + tmp = grub_stpcpy (tmp, " "); + + return buffer; +} + +/* + * This function takes a value string, checks for environmental variables, and + * returns the value string with all environmental variables replaced with the + * value stored in the variable. + */ +static char * +blsuki_expand_val (const char *value) +{ + char *buffer = NULL; + const char *start = value; + const char *end = value; + bool is_env_var = false; + + if (value == NULL) + return NULL; + + while (*value != '\0') + { + if (*value == '$') + { + if (start != end) + { + buffer = blsuki_field_append (is_env_var, buffer, start, end); + if (buffer == NULL) + return NULL; + } + + is_env_var = true; + start = value + 1; + } + else if (is_env_var == true) + { + if (grub_isalnum (*value) == 0 && *value != '_') + { + buffer = blsuki_field_append (is_env_var, buffer, start, end); + is_env_var = false; + start = value; + if (*start == ' ') + start++; + } + } + + end = value; + value++; + } + + if (start != end) + { + buffer = blsuki_field_append (is_env_var, buffer, start, end); + if (buffer == NULL) + return NULL; + } + + return buffer; +} + +/* + * This function returns a string with the command to load a linux kernel with + * kernel command-line options based on what was specified in the BLS config + * file. + */ +static char * +bls_get_linux (grub_blsuki_entry_t *entry) +{ + char *linux_path; + char *linux_cmd = NULL; + char *options = NULL; + char *tmp; + grub_size_t size; + + linux_path = blsuki_get_val (entry, "linux", NULL); + options = blsuki_expand_val (blsuki_get_val (entry, "options", NULL)); + + if (grub_add (sizeof ("linux "), grub_strlen (linux_path), &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size"); + goto finish; + } + + if (options != NULL) + { + if (grub_add (size, grub_strlen (options), &size) || + grub_add (size, 1, &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size"); + goto finish; + } + } + + linux_cmd = grub_malloc (size); + if (linux_cmd == NULL) + goto finish; + + tmp = linux_cmd; + tmp = grub_stpcpy (tmp, "linux "); + tmp = grub_stpcpy (tmp, linux_path); + if (options != NULL) + { + tmp = grub_stpcpy (tmp, " "); + tmp = grub_stpcpy (tmp, options); + } + + tmp = grub_stpcpy (tmp, "\n"); + + finish: + grub_free (options); + return linux_cmd; +} + +/* + * This function returns a string with the command to load all initrds for a + * linux kernel image based on the list provided by the BLS config file. + */ +static char * +bls_get_initrd (grub_blsuki_entry_t *entry) +{ + char **initrd_list; + char *initrd_cmd = NULL; + char *tmp; + grub_size_t size; + int i; + + initrd_list = blsuki_make_list (entry, "initrd", NULL); + if (initrd_list != NULL) + { + size = sizeof ("initrd"); + + for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++) + { + if (grub_add (size, 1, &size) || + grub_add (size, grub_strlen (initrd_list[i]), &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size"); + goto finish; + } + } + + if (grub_add (size, 1, &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size"); + goto finish; + } + + initrd_cmd = grub_malloc (size); + if (initrd_cmd == NULL) + goto finish; + + tmp = grub_stpcpy (initrd_cmd, "initrd"); + for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++) + { + grub_dprintf ("blsuki", "adding initrd %s\n", initrd_list[i]); + tmp = grub_stpcpy (tmp, " "); + tmp = grub_stpcpy (tmp, initrd_list[i]); + } + tmp = grub_stpcpy (tmp, "\n"); + } + + finish: + grub_free (initrd_list); + return initrd_cmd; +} + +/* + * This function returns a string with the command to load a device tree blob + * from the BLS config file. + */ +static char * +bls_get_devicetree (grub_blsuki_entry_t *entry) +{ + char *dt_path; + char *dt_cmd = NULL; + char *tmp; + grub_size_t size; + + dt_path = blsuki_expand_val (blsuki_get_val (entry, "devicetree", NULL)); + if (dt_path != NULL) + { + if (grub_add (sizeof ("devicetree "), grub_strlen (dt_path), &size) || + grub_add (size, 1, &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating device tree buffer size"); + return NULL; + } + + dt_cmd = grub_malloc (size); + if (dt_cmd == NULL) + return NULL; + + tmp = dt_cmd; + tmp = grub_stpcpy (dt_cmd, "devicetree "); + tmp = grub_stpcpy (tmp, dt_path); + tmp = grub_stpcpy (tmp, "\n"); + } + + return dt_cmd; +} + +/* + * This function puts together all of the commands generated from the contents + * of the BLS config file and creates a new entry in the GRUB boot menu. + */ +static void +bls_create_entry (grub_blsuki_entry_t *entry) +{ + int argc = 0; + const char **argv = NULL; + char *title = NULL; + char *linux_path = NULL; + char *linux_cmd = NULL; + char *initrd_cmd = NULL; + char *dt_cmd = NULL; + char *id = entry->filename; + grub_size_t id_len; + char *hotkey = NULL; + char *users = NULL; + char **classes = NULL; + char **args = NULL; + char *src = NULL; + int i; + grub_size_t size; + bool blsuki_save_default; + + linux_path = blsuki_get_val (entry, "linux", NULL); + if (linux_path == NULL) + { + grub_dprintf ("blsuki", "Skipping file %s with no 'linux' key.\n", entry->filename); + goto finish; + } + + id_len = grub_strlen (id); + if (id_len >= BLS_EXT_LEN && grub_strcmp (id + id_len - BLS_EXT_LEN, ".conf") == 0) + id[id_len - BLS_EXT_LEN] = '\0'; + + title = blsuki_get_val (entry, "title", NULL); + hotkey = blsuki_get_val (entry, "grub_hotkey", NULL); + users = blsuki_expand_val (blsuki_get_val (entry, "grub_users", NULL)); + classes = blsuki_make_list (entry, "grub_class", NULL); + args = blsuki_make_list (entry, "grub_arg", &argc); + + argc++; + if (grub_mul (argc + 1, sizeof (char *), &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected creating argv list")); + goto finish; + } + + argv = grub_malloc (size); + if (argv == NULL) + goto finish; + + argv[0] = (title != NULL) ? title : linux_path; + for (i = 1; i < argc; i++) + argv[i] = args[i - 1]; + argv[argc] = NULL; + + linux_cmd = bls_get_linux (entry); + if (linux_cmd == NULL) + goto finish; + + initrd_cmd = bls_get_initrd (entry); + if (grub_errno != GRUB_ERR_NONE) + goto finish; + + dt_cmd = bls_get_devicetree (entry); + if (grub_errno != GRUB_ERR_NONE) + goto finish; + + blsuki_save_default = grub_env_get_bool ("blsuki_save_default", false); + src = grub_xasprintf ("%s%s%s%s", + blsuki_save_default ? "savedefault\n" : "", + linux_cmd, initrd_cmd ? initrd_cmd : "", + dt_cmd ? dt_cmd : ""); + + grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, entry); + + finish: + grub_free (linux_cmd); + grub_free (dt_cmd); + grub_free (initrd_cmd); + grub_free (classes); + grub_free (args); + grub_free (argv); + grub_free (src); +} + +/* + * This function fills a find_entry_info struct passed in by the info parameter. + * If the dirname or devid parameters are set to NULL, the dirname and devid + * fields in the info parameter will be set to default values. If info already + * has a value in the dev fields, we can compare it to the value passed in by + * the devid parameter or the default devid to see if we need to open a new + * device. + */ +static grub_err_t +blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, const char *devid) +{ + grub_device_t dev; + grub_fs_t fs; + + if (info == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "info parameter is not set"); + + if (devid == NULL) + { + devid = grub_env_get ("root"); + if (devid == NULL) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable '%s' isn't set"), "root"); + } + + /* Check that we aren't closing and opening the same device. */ + if (info->dev != NULL && grub_strcmp (info->devid, devid) != 0) + { + grub_device_close (info->dev); + info->dev = NULL; + } + /* If we are using the same device, then we can skip this step and only set the directory. */ + if (info->dev == NULL) + { + grub_dprintf ("blsuki", "opening %s\n", devid); + dev = grub_device_open (devid); + if (dev == NULL) + return grub_errno; + + grub_dprintf ("blsuki", "probing fs\n"); + fs = grub_fs_probe (dev); + if (fs == NULL) + { + grub_device_close (dev); + return grub_errno; + } + + info->devid = devid; + info->dev = dev; + info->fs = fs; + } + + info->dirname = dirname; + + return GRUB_ERR_NONE; +} + +/* + * This function searches for BLS config files based on the data in the info + * parameter. If the fallback option is enabled, the default location will be + * checked for BLS config files if the first attempt fails. + */ +static void +blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) +{ + struct read_entry_info read_entry_info; + grub_fs_t dir_fs = NULL; + grub_device_t dir_dev = NULL; + bool fallback = false; + int r; + + do + { + read_entry_info.file = NULL; + read_entry_info.dirname = info->dirname; + + grub_dprintf ("blsuki", "scanning dir: %s\n", info->dirname); + dir_dev = info->dev; + dir_fs = info->fs; + read_entry_info.devid = info->devid; + + r = dir_fs->fs_dir (dir_dev, read_entry_info.dirname, blsuki_read_entry, + &read_entry_info); + if (r != 0) + { + grub_dprintf ("blsuki", "blsuki_read_entry returned error\n"); + grub_errno = GRUB_ERR_NONE; + } + + /* + * If we aren't able to find BLS entries in the directory given by info->dirname, + * we can fallback to the default location "/boot/loader/entries/" and see if we + * can find the files there. + */ + if (entries == NULL && fallback == false && enable_fallback == true) + { + blsuki_set_find_entry_info (info, GRUB_BLS_CONFIG_PATH, NULL); + grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n", + read_entry_info.dirname, info->dirname); + fallback = true; + } + else + fallback = false; + } + while (fallback == true); +} + +static grub_err_t +blsuki_load_entries (char *path, bool enable_fallback) +{ + grub_size_t len; + static grub_err_t r; + const char *devid = NULL; + char *dir = NULL; + struct find_entry_info info = { + .dev = NULL, + .fs = NULL, + .dirname = NULL, + }; + struct read_entry_info rei = { + .devid = NULL, + .dirname = NULL, + }; + + if (path != NULL) + { + len = grub_strlen (path); + if (len >= BLS_EXT_LEN && grub_strcmp (path + len - BLS_EXT_LEN, ".conf") == 0) + { + rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG); + if (rei.file == NULL) + return grub_errno; + + /* blsuki_read_entry() closes the file. */ + return blsuki_read_entry (path, NULL, &rei); + } + else if (path[0] == '(') + { + devid = path + 1; + + dir = grub_strchr (path, ')'); + if (dir == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid file name `%s'"), path); + + *dir = '\0'; + + /* Check if there is more than the devid in the path. */ + if (dir + 1 < path + len) + dir = dir + 1; + } + else if (path[0] == '/') + dir = path; + } + + if (dir == NULL) + dir = (char *) GRUB_BLS_CONFIG_PATH; + + r = blsuki_set_find_entry_info (&info, dir, devid); + if (r == GRUB_ERR_NONE) + blsuki_find_entry (&info, enable_fallback); + + if (info.dev != NULL) + grub_device_close (info.dev); + + return r; +} + +static bool +blsuki_is_default_entry (const char *def_entry, grub_blsuki_entry_t *entry, int idx) +{ + const char *title; + const char *def_entry_end; + long def_idx; + + if (def_entry == NULL || *def_entry == '\0') + return false; + + if (grub_strcmp (def_entry, entry->filename) == 0) + return true; + + title = blsuki_get_val (entry, "title", NULL); + + if (title != NULL && grub_strcmp (def_entry, title) == 0) + return true; + + def_idx = grub_strtol (def_entry, &def_entry_end, 0); + if (*def_entry_end != '\0' || def_idx < 0 || def_idx > GRUB_INT_MAX) + return false; + + if ((int) def_idx == idx) + return true; + + return false; +} + +/* + * This function creates a GRUB boot menu entry for each BLS entry in the + * entries list. + */ +static grub_err_t +blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id) +{ + const char *def_entry = NULL; + grub_blsuki_entry_t *entry = NULL; + int idx = 0; + + def_entry = grub_env_get ("default"); + + FOR_BLSUKI_ENTRIES(entry) + { + if (entry->visible == true) + { + idx++; + continue; + } + if ((show_default == true && blsuki_is_default_entry (def_entry, entry, idx) == true) || + (show_non_default == true && blsuki_is_default_entry (def_entry, entry, idx) == false) || + (entry_id != NULL && grub_strcmp (entry_id, entry->filename) == 0)) + { + bls_create_entry (entry); + entry->visible = true; + } + + idx++; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + grub_err_t err; + struct grub_arg_list *state = ctxt->state; + char *path = NULL; + char *entry_id = NULL; + bool enable_fallback = false; + bool show_default = false; + bool show_non_default = false; + bool all = true; + entries = NULL; + + if (state[0].set) + path = state[0].arg; + if (state[1].set) + enable_fallback = true; + if (state[2].set) + { + show_default = true; + all = false; + } + if (state[3].set) + { + show_non_default = true; + all = false; + } + if (state[4].set) + { + entry_id = state[4].arg; + all = false; + } + if (all == true) + { + show_default = true; + show_non_default = true; + } + + err = blsuki_load_entries (path, enable_fallback); + if (err != GRUB_ERR_NONE) + return err; + + return blsuki_create_entries (show_default, show_non_default, entry_id); +} + +static grub_extcmd_t bls_cmd; + +GRUB_MOD_INIT(blsuki) +{ + bls_cmd = grub_register_extcmd ("blscfg", grub_cmd_blscfg, 0, + N_("[-p|--path] [-f|--enable-fallback] DIR [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"), + N_("Import Boot Loader Specification snippets."), + bls_opt); +} + +GRUB_MOD_FINI(blsuki) +{ + grub_unregister_extcmd (bls_cmd); +} diff --git a/grub-core/commands/legacycfg.c b/grub-core/commands/legacycfg.c index a768fa6e8..7d9f9eb3c 100644 --- a/grub-core/commands/legacycfg.c +++ b/grub-core/commands/legacycfg.c @@ -143,7 +143,7 @@ legacy_file (const char *filename) args[0] = oldname; grub_normal_add_menu_entry (1, args, NULL, NULL, "legacy", NULL, NULL, - entrysrc, 0); + entrysrc, 0, NULL); grub_free (args); entrysrc[0] = 0; grub_free (oldname); @@ -204,7 +204,7 @@ legacy_file (const char *filename) } args[0] = entryname; grub_normal_add_menu_entry (1, args, NULL, NULL, NULL, - NULL, NULL, entrysrc, 0); + NULL, NULL, entrysrc, 0, NULL); grub_free (args); } diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c index 720e6d8ea..09749c415 100644 --- a/grub-core/commands/menuentry.c +++ b/grub-core/commands/menuentry.c @@ -78,7 +78,7 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes, const char *id, const char *users, const char *hotkey, const char *prefix, const char *sourcecode, - int submenu) + int submenu, grub_blsuki_entry_t *blsuki) { int menu_hotkey = 0; char **menu_args = NULL; @@ -188,6 +188,7 @@ grub_normal_add_menu_entry (int argc, const char **args, (*last)->args = menu_args; (*last)->sourcecode = menu_sourcecode; (*last)->submenu = submenu; + (*last)->blsuki = blsuki; menu->size++; return GRUB_ERR_NONE; @@ -286,7 +287,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) users, ctxt->state[2].arg, 0, ctxt->state[3].arg, - ctxt->extcmd->cmd->name[0] == 's'); + ctxt->extcmd->cmd->name[0] == 's', + NULL); src = args[argc - 1]; args[argc - 1] = NULL; @@ -303,7 +305,7 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) ctxt->state[0].args, ctxt->state[4].arg, users, ctxt->state[2].arg, prefix, src + 1, - ctxt->extcmd->cmd->name[0] == 's'); + ctxt->extcmd->cmd->name[0] == 's', NULL); src[len - 1] = ch; args[argc - 1] = src; diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 96abfda2f..a9e7a09d1 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,11 @@ grub_normal_free_menu (grub_menu_t menu) grub_free (entry->args); } + if (entry->blsuki) + { + entry->blsuki->visible = 0; + } + grub_free ((void *) entry->id); grub_free ((void *) entry->users); grub_free ((void *) entry->title); diff --git a/include/grub/menu.h b/include/grub/menu.h index ee2b5e910..9bbe7ab0f 100644 --- a/include/grub/menu.h +++ b/include/grub/menu.h @@ -20,6 +20,18 @@ #ifndef GRUB_MENU_HEADER #define GRUB_MENU_HEADER 1 +struct grub_blsuki_entry +{ + struct grub_blsuki_entry *next; + struct grub_blsuki_entry **prev; + struct keyval **keyvals; + grub_size_t keyvals_size; + int nkeyvals; + char *filename; + bool visible; +}; +typedef struct grub_blsuki_entry grub_blsuki_entry_t; + struct grub_menu_entry_class { char *name; @@ -60,6 +72,9 @@ struct grub_menu_entry /* The next element. */ struct grub_menu_entry *next; + + /* BLS used to populate the entry */ + grub_blsuki_entry_t *blsuki; }; typedef struct grub_menu_entry *grub_menu_entry_t; diff --git a/include/grub/normal.h b/include/grub/normal.h index 218cbabcc..d0150e3c2 100644 --- a/include/grub/normal.h +++ b/include/grub/normal.h @@ -145,7 +145,7 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes, const char *id, const char *users, const char *hotkey, const char *prefix, const char *sourcecode, - int submenu); + int submenu, grub_blsuki_entry_t *blsuki); grub_err_t grub_normal_set_password (const char *user, const char *password); From 51b9601320dca01bada22804058f5ca8163089d3 Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Tue, 12 Aug 2025 03:45:34 +0000 Subject: [PATCH 014/208] util/misc.c: Change offset type for grub_util_write_image_at() Adding filevercmp support to grub-core/commands/blsuki.c from gnulib will cause issues with the type of the offset parameter for grub_util_write_image_at() for emu builds. To fix this issue, we can change the type from off_t to grub_off_t. Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- include/grub/util/misc.h | 2 +- util/misc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/grub/util/misc.h b/include/grub/util/misc.h index e9e0a6724..bfce06558 100644 --- a/include/grub/util/misc.h +++ b/include/grub/util/misc.h @@ -36,7 +36,7 @@ char *grub_util_read_image (const char *path); void grub_util_load_image (const char *path, char *buf); void grub_util_write_image (const char *img, size_t size, FILE *out, const char *name); -void grub_util_write_image_at (const void *img, size_t size, off_t offset, +void grub_util_write_image_at (const void *img, size_t size, grub_off_t offset, FILE *out, const char *name); char *make_system_path_relative_to_its_root (const char *path); diff --git a/util/misc.c b/util/misc.c index 0f928e5b4..6e16a68d9 100644 --- a/util/misc.c +++ b/util/misc.c @@ -101,7 +101,7 @@ grub_util_read_image (const char *path) } void -grub_util_write_image_at (const void *img, size_t size, off_t offset, FILE *out, +grub_util_write_image_at (const void *img, size_t size, grub_off_t offset, FILE *out, const char *name) { grub_util_info ("writing 0x%" GRUB_HOST_PRIxLONG_LONG " bytes at offset 0x%" From 5190df8510fb4ef762497902ddbfdcccbb56404a Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Tue, 12 Aug 2025 03:45:35 +0000 Subject: [PATCH 015/208] blsuki: Check for mounted /boot in emu Irritatingly, BLS defines paths relative to the mountpoint of the filesystem which contains its snippets, not / or any other fixed location. So grub-emu needs to know whether /boot is a separate filesystem from / and conditionally prepend a path. Signed-off-by: Robbie Harwood Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/Makefile.core.def | 3 ++ grub-core/commands/blsuki.c | 83 ++++++++++++++++++++++++++++++--- grub-core/osdep/linux/getroot.c | 8 ++++ grub-core/osdep/unix/getroot.c | 4 +- include/grub/emu/misc.h | 2 +- 5 files changed, 91 insertions(+), 9 deletions(-) diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 6bd5f2c28..0fcf67f9d 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -369,6 +369,9 @@ kernel = { emu = kern/emu/cache_s.S; emu = kern/emu/hostdisk.c; emu = osdep/unix/hostdisk.c; + emu = osdep/relpath.c; + emu = osdep/getroot.c; + emu = osdep/unix/getroot.c; emu = osdep/exec.c; extra_dist = osdep/unix/exec.c; emu = osdep/devmapper/hostdisk.c; diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c index 9440adb10..ed93a150b 100644 --- a/grub-core/commands/blsuki.c +++ b/grub-core/commands/blsuki.c @@ -32,6 +32,13 @@ #include #include +#ifdef GRUB_MACHINE_EMU +#include +#define GRUB_BOOT_DEVICE "/boot" +#else +#define GRUB_BOOT_DEVICE "" +#endif + GRUB_MOD_LICENSE ("GPLv3+"); #define GRUB_BLS_CONFIG_PATH "/loader/entries/" @@ -79,6 +86,34 @@ static grub_blsuki_entry_t *entries; #define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries) +/* + * BLS appears to make paths relative to the filesystem that snippets are + * on, not /. Attempt to cope. + */ +static char *blsuki_update_boot_device (char *tmp) +{ +#ifdef GRUB_MACHINE_EMU + static int separate_boot = -1; + char *ret; + + if (separate_boot != -1) + goto probed; + + separate_boot = 0; + + ret = grub_make_system_path_relative_to_its_root (GRUB_BOOT_DEVICE); + + if (ret != NULL && ret[0] == '\0') + separate_boot = 1; + + probed: + if (separate_boot == 0) + return tmp; +#endif + + return grub_stpcpy (tmp, GRUB_BOOT_DEVICE); +} + /* * This function will add a new keyval pair to a list of keyvals stored in the * entry parameter. @@ -526,7 +561,7 @@ bls_get_linux (grub_blsuki_entry_t *entry) linux_path = blsuki_get_val (entry, "linux", NULL); options = blsuki_expand_val (blsuki_get_val (entry, "options", NULL)); - if (grub_add (sizeof ("linux "), grub_strlen (linux_path), &size)) + if (grub_add (sizeof ("linux " GRUB_BOOT_DEVICE), grub_strlen (linux_path), &size)) { grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size"); goto finish; @@ -548,6 +583,7 @@ bls_get_linux (grub_blsuki_entry_t *entry) tmp = linux_cmd; tmp = grub_stpcpy (tmp, "linux "); + tmp = blsuki_update_boot_device (tmp); tmp = grub_stpcpy (tmp, linux_path); if (options != NULL) { @@ -582,7 +618,7 @@ bls_get_initrd (grub_blsuki_entry_t *entry) for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++) { - if (grub_add (size, 1, &size) || + if (grub_add (size, sizeof (" " GRUB_BOOT_DEVICE) - 1, &size) || grub_add (size, grub_strlen (initrd_list[i]), &size)) { grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size"); @@ -605,6 +641,7 @@ bls_get_initrd (grub_blsuki_entry_t *entry) { grub_dprintf ("blsuki", "adding initrd %s\n", initrd_list[i]); tmp = grub_stpcpy (tmp, " "); + tmp = blsuki_update_boot_device (tmp); tmp = grub_stpcpy (tmp, initrd_list[i]); } tmp = grub_stpcpy (tmp, "\n"); @@ -630,7 +667,7 @@ bls_get_devicetree (grub_blsuki_entry_t *entry) dt_path = blsuki_expand_val (blsuki_get_val (entry, "devicetree", NULL)); if (dt_path != NULL) { - if (grub_add (sizeof ("devicetree "), grub_strlen (dt_path), &size) || + if (grub_add (sizeof ("devicetree " GRUB_BOOT_DEVICE), grub_strlen (dt_path), &size) || grub_add (size, 1, &size)) { grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating device tree buffer size"); @@ -643,6 +680,7 @@ bls_get_devicetree (grub_blsuki_entry_t *entry) tmp = dt_cmd; tmp = grub_stpcpy (dt_cmd, "devicetree "); + tmp = blsuki_update_boot_device (tmp); tmp = grub_stpcpy (tmp, dt_path); tmp = grub_stpcpy (tmp, "\n"); } @@ -757,7 +795,11 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c if (devid == NULL) { +#ifdef GRUB_MACHINE_EMU + devid = "host"; +#else devid = grub_env_get ("root"); +#endif if (devid == NULL) return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable '%s' isn't set"), "root"); } @@ -799,10 +841,13 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c * parameter. If the fallback option is enabled, the default location will be * checked for BLS config files if the first attempt fails. */ -static void +static grub_err_t blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) { struct read_entry_info read_entry_info; + char *default_dir = NULL; + char *tmp; + grub_size_t default_size; grub_fs_t dir_fs = NULL; grub_device_t dir_dev = NULL; bool fallback = false; @@ -833,7 +878,15 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) */ if (entries == NULL && fallback == false && enable_fallback == true) { - blsuki_set_find_entry_info (info, GRUB_BLS_CONFIG_PATH, NULL); + default_size = sizeof (GRUB_BOOT_DEVICE) + sizeof (GRUB_BLS_CONFIG_PATH) - 1; + default_dir = grub_malloc (default_size); + if (default_dir == NULL) + return grub_errno; + + tmp = blsuki_update_boot_device (default_dir); + tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH); + + blsuki_set_find_entry_info (info, default_dir, NULL); grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n", read_entry_info.dirname, info->dirname); fallback = true; @@ -842,6 +895,9 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) fallback = false; } while (fallback == true); + + grub_free (default_dir); + return GRUB_ERR_NONE; } static grub_err_t @@ -851,6 +907,9 @@ blsuki_load_entries (char *path, bool enable_fallback) static grub_err_t r; const char *devid = NULL; char *dir = NULL; + char *default_dir = NULL; + char *tmp; + grub_size_t dir_size; struct find_entry_info info = { .dev = NULL, .fs = NULL, @@ -892,15 +951,25 @@ blsuki_load_entries (char *path, bool enable_fallback) } if (dir == NULL) - dir = (char *) GRUB_BLS_CONFIG_PATH; + { + dir_size = sizeof (GRUB_BOOT_DEVICE) + sizeof (GRUB_BLS_CONFIG_PATH) - 2; + default_dir = grub_malloc (dir_size); + if (default_dir == NULL) + return grub_errno; + + tmp = blsuki_update_boot_device (default_dir); + tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH); + dir = default_dir; + } r = blsuki_set_find_entry_info (&info, dir, devid); if (r == GRUB_ERR_NONE) - blsuki_find_entry (&info, enable_fallback); + r = blsuki_find_entry (&info, enable_fallback); if (info.dev != NULL) grub_device_close (info.dev); + grub_free (default_dir); return r; } diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c index 7872a8d03..38fe110fe 100644 --- a/grub-core/osdep/linux/getroot.c +++ b/grub-core/osdep/linux/getroot.c @@ -131,6 +131,7 @@ struct mountinfo_entry char fstype[ESCAPED_PATH_MAX + 1], device[ESCAPED_PATH_MAX + 1]; }; +#ifdef GRUB_UTIL static char ** grub_util_raid_getmembers (const char *name, int bootable) { @@ -191,6 +192,7 @@ grub_util_raid_getmembers (const char *name, int bootable) return devicelist; } +#endif /* Statting something on a btrfs filesystem always returns a virtual device major/minor pair rather than the real underlying device, because btrfs @@ -579,6 +581,7 @@ out: return ret; } +#ifdef GRUB_UTIL static char * get_mdadm_uuid (const char *os_dev) { @@ -636,6 +639,7 @@ out: return name; } +#endif static int grub_util_is_imsm_or_ddf (const char *os_dev) @@ -975,6 +979,7 @@ grub_util_part_to_disk (const char *os_dev, struct stat *st, return path; } +#ifdef GRUB_UTIL static char * grub_util_get_raid_grub_dev (const char *os_dev) { @@ -1077,6 +1082,7 @@ grub_util_get_raid_grub_dev (const char *os_dev) } return grub_dev; } +#endif enum grub_dev_abstraction_types grub_util_get_dev_abstraction_os (const char *os_dev) @@ -1093,6 +1099,7 @@ grub_util_get_dev_abstraction_os (const char *os_dev) return GRUB_DEV_ABSTRACTION_NONE; } +#ifdef GRUB_UTIL int grub_util_pull_device_os (const char *os_dev, enum grub_dev_abstraction_types ab) @@ -1149,6 +1156,7 @@ grub_util_get_grub_dev_os (const char *os_dev) return grub_dev; } +#endif char * grub_make_system_path_relative_to_its_root_os (const char *path) diff --git a/grub-core/osdep/unix/getroot.c b/grub-core/osdep/unix/getroot.c index c7aa202ab..c061735a4 100644 --- a/grub-core/osdep/unix/getroot.c +++ b/grub-core/osdep/unix/getroot.c @@ -16,8 +16,8 @@ * along with GRUB. If not, see . */ -#include #include +#include #include #include @@ -566,6 +566,7 @@ grub_guess_root_devices (const char *dir_in) #endif +#ifdef GRUB_UTIL void grub_util_pull_lvm_by_command (const char *os_dev) { @@ -662,6 +663,7 @@ out: free (buf); free (vgid); } +#endif /* ZFS has similar problems to those of btrfs (see above). */ void diff --git a/include/grub/emu/misc.h b/include/grub/emu/misc.h index fefbec499..ceedc7399 100644 --- a/include/grub/emu/misc.h +++ b/include/grub/emu/misc.h @@ -39,7 +39,7 @@ void grub_fini_all (void); void grub_find_zpool_from_dir (const char *dir, char **poolname, char **poolfs); -char *grub_make_system_path_relative_to_its_root (const char *path) +char *EXPORT_FUNC (grub_make_system_path_relative_to_its_root) (const char *path) WARN_UNUSED_RESULT; int grub_util_device_is_mapped (const char *dev); From 14c2966c725774aa58a811dada3277c7095cbcb3 Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Tue, 12 Aug 2025 03:45:36 +0000 Subject: [PATCH 016/208] blsuki: Add uki command to load Unified Kernel Image entries A Unified Kernel Image (UKI) is a single UEFI PE file that combines a UEFI boot stub, a Linux kernel image, an initrd, and further resources. The uki command will locate where the UKI file is and create a GRUB menu entry to load it. The Unified Kernel Image Specification: https://uapi-group.org/specifications/specs/unified_kernel_image/ Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- docs/grub.texi | 36 ++- grub-core/commands/blsuki.c | 474 +++++++++++++++++++++++++++++++++--- include/grub/menu.h | 2 + 3 files changed, 473 insertions(+), 39 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index e838177cd..57667e803 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -3350,7 +3350,8 @@ chain-loaded system, @pxref{drivemap}. @subsection blsuki_save_default If this variable is set, menu entries generated from BLS config files -(@pxref{blscfg}) will be set as the default boot entry when selected. +(@pxref{blscfg}) or UKI files (@pxref{uki}) will be set as the default boot +entry when selected. @node check_signatures @@ -6555,6 +6556,7 @@ you forget a command, you can run the command @command{help} * tpm2_dump_pcr:: Dump TPM2 PCRs * true:: Do nothing, successfully * trust:: Add public key to list of trusted keys +* uki:: Load Unified Kernel Image menu entries * unset:: Unset an environment variable @comment * vbeinfo:: List available video modes * verify_detached:: Verify detached digital signature @@ -8307,6 +8309,38 @@ Unset the environment variable @var{envvar}. @end deffn +@node uki +@subsection uki + +@deffn Command uki [@option{-p|--path} dir] [@option{-f|--enable-fallback}] [@option{-d|--show-default}] [@option{-n|--show-non-default}] [@option{-e|--entry} file] +Load Unified Kernel Image (UKI) files into the GRUB menu. Boot entries +generated from @command{uki} won't interfere with entries from @file{grub.cfg} appearing in the +GRUB menu. Also, entries generated from @command{uki} exists only in memory and don't +update @file{grub.cfg}. + +By default, the UKI files are stored in the @file{/EFI/Linux} directory in the EFI +system partition. If UKI files are stored elsewhere, the @option{--path} option can be +used to check a different directory instead of the default location. If no UKI +files are found while using the @option{--path} option, the @option{--enable-fallback} option can +be used to check for files in the default location. + +The @option{--show-default} option allows the default boot entry to be added to the +GRUB menu from the UKI files. + +The @option{--show-non-default} option allows non-default boot entries to be added to +the GRUB menu from the UKI files. + +The @option{--entry} option allows specific boot entries to be added to the GRUB menu +from the UKI files. + +The @option{--entry}, @option{--show-default}, and @option{--show-non-default} options +are used to filter which UKI files are added to the GRUB menu. If none are +used, all files in the default location or the location specified by @option{--path} +will be added to the GRUB menu. + +For more information on UKI, see: @uref{https://uapi-group.org/specifications/specs/unified_kernel_image/, The Unified Kernel Image Specification} +@end deffn + @ignore @node vbeinfo @subsection vbeinfo diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c index ed93a150b..d089dec62 100644 --- a/grub-core/commands/blsuki.c +++ b/grub-core/commands/blsuki.c @@ -32,6 +32,12 @@ #include #include +#ifdef GRUB_MACHINE_EFI +#include +#include +#include +#endif + #ifdef GRUB_MACHINE_EMU #include #define GRUB_BOOT_DEVICE "/boot" @@ -42,14 +48,28 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define GRUB_BLS_CONFIG_PATH "/loader/entries/" +#define GRUB_UKI_CONFIG_PATH "/EFI/Linux" #define BLS_EXT_LEN (sizeof (".conf") - 1) +#define UKI_EXT_LEN (sizeof (".efi") - 1) /* * It is highly unlikely to ever receive a large amount of keyval pairs. A * limit of 10000 is more than enough. */ #define BLSUKI_KEYVALS_MAX 10000 +/* + * The only sections we read are ".cmdline" and ".osrel". The ".cmdline" + * section has a size limit of 4096 and it would be very unlikely for the size + * of the ".osrel" section to be 5 times larger than 4096. + */ +#define UKI_SECTION_SIZE_MAX (5 * 4096) + +enum blsuki_cmd_type + { + BLSUKI_BLS_CMD, + BLSUKI_UKI_CMD, + }; static const struct grub_arg_option bls_opt[] = { @@ -61,6 +81,18 @@ static const struct grub_arg_option bls_opt[] = {0, 0, 0, 0, 0, 0} }; +#ifdef GRUB_MACHINE_EFI +static const struct grub_arg_option uki_opt[] = + { + {"path", 'p', 0, N_("Specify path to find UKI entries."), N_("DIR"), ARG_TYPE_PATHNAME}, + {"enable-fallback", 'f', 0, "Fallback to the default BLS path if --path fails to find UKI entries.", 0, ARG_TYPE_NONE}, + {"show-default", 'd', 0, N_("Allow the default UKI entry to be added to the GRUB menu."), 0, ARG_TYPE_NONE}, + {"show-non-default", 'n', 0, N_("Allow the non-default UKI entries to be added to the GRUB menu."), 0, ARG_TYPE_NONE}, + {"entry", 'e', 0, N_("Allow specificUKII entries to be added to the GRUB menu."), N_("FILE"), ARG_TYPE_FILE}, + {0, 0, 0, 0, 0, 0} + }; +#endif + struct keyval { const char *key; @@ -71,6 +103,7 @@ struct read_entry_info { const char *devid; const char *dirname; + enum blsuki_cmd_type cmd_type; grub_file_t file; }; @@ -82,7 +115,7 @@ struct find_entry_info grub_fs_t fs; }; -static grub_blsuki_entry_t *entries; +static grub_blsuki_entry_t *entries = NULL; #define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries) @@ -181,7 +214,7 @@ blsuki_add_keyval (grub_blsuki_entry_t *entry, char *key, char *val) * Find the value of the key named by keyname. If there are allowed to be * more than one, pass a pointer set to -1 to the last parameter the first * time, and pass the same pointer through each time after, and it'll return - * them in sorted order as defined in the BLS fragment file. + * them in sorted order. */ static char * blsuki_get_val (grub_blsuki_entry_t *entry, const char *keyname, int *last) @@ -310,20 +343,212 @@ bls_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry) return err; } +#ifdef GRUB_MACHINE_EFI +/* + * This function searches for the .cmdline, .osrel, and .linux sections of a + * UKI. We only need to store the data for the .cmdline and .osrel sections, + * but we also need to verify that the .linux section exists. + */ +static grub_err_t +uki_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry) +{ + struct grub_msdos_image_header *dos = NULL; + struct grub_pe_image_header *pe = NULL; + grub_off_t section_offset = 0; + struct grub_pe32_section_table *section = NULL; + struct grub_pe32_coff_header *coff_header = NULL; + char *val = NULL; + char *key = NULL; + const char *target[] = {".cmdline", ".osrel", ".linux", NULL}; + bool has_linux = false; + grub_err_t err = GRUB_ERR_NONE; + + dos = grub_zalloc (sizeof (*dos)); + if (dos == NULL) + return grub_errno; + if (grub_file_read (f, dos, sizeof (*dos)) < (grub_ssize_t) sizeof (*dos)) + { + err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read UKI image header"); + goto finish; + } + if (dos->msdos_magic != GRUB_PE32_MAGIC) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "plain image kernel is not supported"); + goto finish; + } + + grub_dprintf ("blsuki", "PE/COFF header @ %08x\n", dos->pe_image_header_offset); + pe = grub_zalloc (sizeof (*pe)); + if (pe == NULL) + { + err = grub_errno; + goto finish; + } + if (grub_file_seek (f, dos->pe_image_header_offset) == (grub_off_t) -1 || + grub_file_read (f, pe, sizeof (*pe)) != sizeof (*pe)) + { + err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read COFF image header"); + goto finish; + } + if (pe->optional_header.magic != GRUB_PE32_NATIVE_MAGIC) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "non-native image not supported"); + goto finish; + } + + coff_header = &(pe->coff_header); + section_offset = dos->pe_image_header_offset + sizeof (*pe); + + for (int i = 0; i < coff_header->num_sections; i++) + { + section = grub_zalloc (sizeof (*section)); + if (section == NULL) + { + err = grub_errno; + goto finish; + } + + if (grub_file_seek (f, section_offset) == (grub_off_t) -1 || + grub_file_read (f, section, sizeof (*section)) != sizeof (*section)) + { + err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read section header"); + goto finish; + } + + key = grub_strndup (section->name, 8); + if (key == NULL) + { + err = grub_errno; + goto finish; + } + + for (int j = 0; target[j] != NULL; j++) + { + if (grub_strcmp (key, target[j]) == 0) + { + /* + * We don't need to read the contents of the .linux PE section, but we + * should verify that the section exists. + */ + if (grub_strcmp (key, ".linux") == 0) + { + has_linux = true; + break; + } + + if (section->raw_data_size > UKI_SECTION_SIZE_MAX) + { + err = grub_error (GRUB_ERR_BAD_NUMBER, "UKI section size is larger than expected"); + goto finish; + } + + val = grub_zalloc (section->raw_data_size); + if (val == NULL) + { + err = grub_errno; + goto finish; + } + + if (grub_file_seek (f, section->raw_data_offset) == (grub_off_t) -1 || + grub_file_read (f, val, section->raw_data_size) != (grub_ssize_t) section->raw_data_size) + { + err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read section"); + goto finish; + } + + err = blsuki_add_keyval (entry, key, val); + if (err != GRUB_ERR_NONE) + goto finish; + + break; + } + } + + section_offset += sizeof (*section); + grub_free (section); + grub_free (val); + grub_free (key); + section = NULL; + val = NULL; + key = NULL; + } + + if (has_linux == false) + err = grub_error (GRUB_ERR_NO_KERNEL, "UKI is missing the '.linux' section"); + + finish: + grub_free (dos); + grub_free (pe); + grub_free (section); + grub_free (val); + grub_free (key); + return err; +} + +/* + * This function obtains the keyval pairs when the .osrel data is input into + * the osrel_ptr parameter and returns the keyval pair. Since we are using + * grub_strtok_r(), the osrel_ptr will be updated to the following line of + * osrel. This function returns NULL when it reaches the end of osrel. + */ +static char * +uki_read_osrel (char **osrel_ptr, char **val_ret) +{ + char *key, *val; + grub_size_t val_size; + + for (;;) + { + key = grub_strtok_r (NULL, "\n\r", osrel_ptr); + if (key == NULL) + return NULL; + + /* Remove leading white space */ + while (*key == ' ' || *key == '\t') + key++; + + /* Skip commented lines */ + if (*key == '#') + continue; + + /* Split key/value */ + key = grub_strtok_r (key, "=", &val); + if (key == NULL || *val == '\0') + continue; + + /* Remove quotes from value */ + val_size = grub_strlen (val); + if ((*val == '\"' && val[val_size - 1] == '\"') || + (*val == '\'' && val[val_size - 1] == '\'')) + { + val[val_size - 1] = '\0'; + val++; + } + + *val_ret = val; + break; + } + + return key; +} +#endif + /* * If a file hasn't already been opened, this function opens a BLS config file - * and initializes entry data before parsing keyvals and adding the entry to - * the list of BLS entries. + * or UKI and initializes entry data before parsing keyvals and adding the entry + * to the list of BLS or UKI entries. */ static int blsuki_read_entry (const char *filename, const struct grub_dirhook_info *dirhook_info __attribute__ ((__unused__)), void *data) { - grub_size_t path_len = 0, filename_len; - grub_err_t err; + grub_size_t path_len = 0, ext_len = 0, filename_len; + grub_err_t err = GRUB_ERR_NONE; char *p = NULL; + const char *ext = NULL; grub_file_t f = NULL; + enum grub_file_type file_type = 0; grub_blsuki_entry_t *entry; struct read_entry_info *info = (struct read_entry_info *) data; @@ -331,17 +556,31 @@ blsuki_read_entry (const char *filename, filename_len = grub_strlen (filename); + if (info->cmd_type == BLSUKI_BLS_CMD) + { + ext = ".conf"; + ext_len = BLS_EXT_LEN; + file_type = GRUB_FILE_TYPE_CONFIG; + } +#ifdef GRUB_MACHINE_EFI + else if (info->cmd_type == BLSUKI_UKI_CMD) + { + ext = ".efi"; + ext_len = UKI_EXT_LEN; + file_type = GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE; + } +#endif + if (info->file != NULL) f = info->file; else { - if (filename_len < BLS_EXT_LEN || - grub_strcmp (filename + filename_len - BLS_EXT_LEN, ".conf") != 0) + if (filename_len < ext_len || + grub_strcmp (filename + filename_len - ext_len, ext) != 0) return 0; p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename); - - f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG); + f = grub_file_open (p, file_type); grub_free (p); if (f == NULL) goto finish; @@ -373,7 +612,26 @@ blsuki_read_entry (const char *filename, goto finish; } - err = bls_parse_keyvals (f, entry); + entry->dirname = grub_strdup (info->dirname); + if (entry->dirname == NULL) + { + grub_free (entry); + goto finish; + } + + entry->devid = grub_strdup (info->devid); + if (entry->devid == NULL) + { + grub_free (entry); + goto finish; + } + + if (info->cmd_type == BLSUKI_BLS_CMD) + err = bls_parse_keyvals (f, entry); +#ifdef GRUB_MACHINE_EFI + else if (info->cmd_type == BLSUKI_UKI_CMD) + err = uki_parse_keyvals (f, entry); +#endif if (err == GRUB_ERR_NONE) blsuki_add_entry (entry); @@ -389,7 +647,7 @@ blsuki_read_entry (const char *filename, /* * This function returns a list of values that had the same key in the BLS - * config file. The number of entries in this list is returned by the len + * config file or UKI. The number of entries in this list is returned by the len * parameter. */ static char ** @@ -776,6 +1034,70 @@ bls_create_entry (grub_blsuki_entry_t *entry) grub_free (src); } +#ifdef GRUB_MACHINE_EFI +/* + * This function puts together the section data received from the UKI and + * generates a new entry in the GRUB boot menu. + */ +static void +uki_create_entry (grub_blsuki_entry_t *entry) +{ + const char **argv = NULL; + char *id = entry->filename; + char *title = NULL; + char *options = NULL; + char *osrel, *osrel_line; + char *key = NULL; + char *value = NULL; + char *src = NULL; + bool blsuki_save_default; + + /* + * Although .osrel is listed as optional in the UKI specification, the .osrel + * section is needed to generate the GRUB menu entry title. + */ + osrel = blsuki_get_val (entry, ".osrel", NULL); + if (osrel == NULL) + { + grub_dprintf ("blsuki", "Skipping file %s with no '.osrel' key.\n", entry->filename); + goto finish; + } + + osrel_line = osrel; + while ((key = uki_read_osrel (&osrel_line, &value)) != NULL) + { + if (grub_strcmp ("PRETTY_NAME", key) == 0) + { + title = value; + break; + } + } + + options = blsuki_get_val (entry, ".cmdline", NULL); + + argv = grub_zalloc (2 * sizeof (char *)); + if (argv == NULL) + goto finish; + argv[0] = title; + + blsuki_save_default = grub_env_get_bool ("blsuki_save_default", false); + src = grub_xasprintf ("%schainloader (%s)%s/%s%s%s\n", + blsuki_save_default ? "savedefault\n" : "", + entry->devid, entry->dirname, + entry->filename, + (options != NULL) ? " " : "", + (options != NULL) ? options : ""); + + grub_normal_add_menu_entry (1, argv, NULL, id, NULL, NULL, NULL, src, 0, entry); + + finish: + grub_free (argv); + grub_free (src); + grub_free (options); + grub_free (osrel); +} +#endif + /* * This function fills a find_entry_info struct passed in by the info parameter. * If the dirname or devid parameters are set to NULL, the dirname and devid @@ -785,7 +1107,7 @@ bls_create_entry (grub_blsuki_entry_t *entry) * device. */ static grub_err_t -blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, const char *devid) +blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, const char *devid, enum blsuki_cmd_type cmd_type) { grub_device_t dev; grub_fs_t fs; @@ -795,10 +1117,23 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c if (devid == NULL) { + if (cmd_type == BLSUKI_BLS_CMD) + { #ifdef GRUB_MACHINE_EMU - devid = "host"; + devid = "host"; #else - devid = grub_env_get ("root"); + devid = grub_env_get ("root"); +#endif + } +#ifdef GRUB_MACHINE_EFI + else if (cmd_type == BLSUKI_UKI_CMD) + { + grub_efi_loaded_image_t *image = grub_efi_get_loaded_image (grub_efi_image_handle); + + if (image == NULL) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("unable to find boot device")); + devid = grub_efidisk_get_device_name (image->device_handle); + } #endif if (devid == NULL) return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable '%s' isn't set"), "root"); @@ -837,15 +1172,16 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c } /* - * This function searches for BLS config files based on the data in the info - * parameter. If the fallback option is enabled, the default location will be - * checked for BLS config files if the first attempt fails. + * This function searches for BLS config files and UKIs based on the data in the + * info parameter. If the fallback option is enabled, the default location will + * be checked for BLS config files or UKIs if the first attempt fails. */ static grub_err_t -blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) +blsuki_find_entry (struct find_entry_info *info, bool enable_fallback, enum blsuki_cmd_type cmd_type) { struct read_entry_info read_entry_info; char *default_dir = NULL; + const char *cmd_dir = NULL; char *tmp; grub_size_t default_size; grub_fs_t dir_fs = NULL; @@ -862,6 +1198,7 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) dir_dev = info->dev; dir_fs = info->fs; read_entry_info.devid = info->devid; + read_entry_info.cmd_type = cmd_type; r = dir_fs->fs_dir (dir_dev, read_entry_info.dirname, blsuki_read_entry, &read_entry_info); @@ -874,19 +1211,27 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) /* * If we aren't able to find BLS entries in the directory given by info->dirname, * we can fallback to the default location "/boot/loader/entries/" and see if we - * can find the files there. + * can find the files there. If we can't find UKI entries, fallback to + * "/EFI/Linux" on the EFI system partition. */ if (entries == NULL && fallback == false && enable_fallback == true) { - default_size = sizeof (GRUB_BOOT_DEVICE) + sizeof (GRUB_BLS_CONFIG_PATH) - 1; + if (cmd_type == BLSUKI_BLS_CMD) + cmd_dir = GRUB_BLS_CONFIG_PATH; +#ifdef GRUB_MACHINE_EFI + else if (cmd_type == BLSUKI_UKI_CMD) + cmd_dir = GRUB_UKI_CONFIG_PATH; +#endif + + default_size = sizeof (GRUB_BOOT_DEVICE) + grub_strlen (cmd_dir); default_dir = grub_malloc (default_size); if (default_dir == NULL) return grub_errno; tmp = blsuki_update_boot_device (default_dir); - tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH); + tmp = grub_stpcpy (tmp, cmd_dir); - blsuki_set_find_entry_info (info, default_dir, NULL); + blsuki_set_find_entry_info (info, default_dir, NULL, cmd_type); grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n", read_entry_info.dirname, info->dirname); fallback = true; @@ -901,15 +1246,17 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) } static grub_err_t -blsuki_load_entries (char *path, bool enable_fallback) +blsuki_load_entries (char *path, bool enable_fallback, enum blsuki_cmd_type cmd_type) { - grub_size_t len; + grub_size_t len, ext_len = 0; static grub_err_t r; const char *devid = NULL; char *dir = NULL; char *default_dir = NULL; char *tmp; + const char *cmd_dir = NULL; grub_size_t dir_size; + const char *ext = NULL; struct find_entry_info info = { .dev = NULL, .fs = NULL, @@ -918,12 +1265,26 @@ blsuki_load_entries (char *path, bool enable_fallback) struct read_entry_info rei = { .devid = NULL, .dirname = NULL, + .cmd_type = cmd_type, }; if (path != NULL) { + if (cmd_type == BLSUKI_BLS_CMD) + { + ext = ".conf"; + ext_len = BLS_EXT_LEN; + } +#ifdef GRUB_MACHINE_EFI + else if (cmd_type == BLSUKI_UKI_CMD) + { + ext = ".efi"; + ext_len = UKI_EXT_LEN; + } +#endif + len = grub_strlen (path); - if (len >= BLS_EXT_LEN && grub_strcmp (path + len - BLS_EXT_LEN, ".conf") == 0) + if (len >= ext_len && grub_strcmp (path + len - ext_len, ext) == 0) { rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG); if (rei.file == NULL) @@ -952,19 +1313,26 @@ blsuki_load_entries (char *path, bool enable_fallback) if (dir == NULL) { - dir_size = sizeof (GRUB_BOOT_DEVICE) + sizeof (GRUB_BLS_CONFIG_PATH) - 2; + if (cmd_type == BLSUKI_BLS_CMD) + cmd_dir = GRUB_BLS_CONFIG_PATH; +#ifdef GRUB_MACHINE_EFI + else if (cmd_type == BLSUKI_UKI_CMD) + cmd_dir = GRUB_UKI_CONFIG_PATH; +#endif + + dir_size = sizeof (GRUB_BOOT_DEVICE) + grub_strlen (cmd_dir); default_dir = grub_malloc (dir_size); if (default_dir == NULL) return grub_errno; tmp = blsuki_update_boot_device (default_dir); - tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH); + tmp = grub_stpcpy (tmp, cmd_dir); dir = default_dir; } - r = blsuki_set_find_entry_info (&info, dir, devid); + r = blsuki_set_find_entry_info (&info, dir, devid, cmd_type); if (r == GRUB_ERR_NONE) - r = blsuki_find_entry (&info, enable_fallback); + r = blsuki_find_entry (&info, enable_fallback, cmd_type); if (info.dev != NULL) grub_device_close (info.dev); @@ -1002,11 +1370,11 @@ blsuki_is_default_entry (const char *def_entry, grub_blsuki_entry_t *entry, int } /* - * This function creates a GRUB boot menu entry for each BLS entry in the - * entries list. + * This function creates a GRUB boot menu entry for each BLS or UKI entry in + * the entries list. */ static grub_err_t -blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id) +blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id, enum blsuki_cmd_type cmd_type) { const char *def_entry = NULL; grub_blsuki_entry_t *entry = NULL; @@ -1025,7 +1393,12 @@ blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id) (show_non_default == true && blsuki_is_default_entry (def_entry, entry, idx) == false) || (entry_id != NULL && grub_strcmp (entry_id, entry->filename) == 0)) { - bls_create_entry (entry); + if (cmd_type == BLSUKI_BLS_CMD) + bls_create_entry (entry); +#ifdef GRUB_MACHINE_EFI + else if (cmd_type == BLSUKI_UKI_CMD) + uki_create_entry (entry); +#endif entry->visible = true; } @@ -1036,8 +1409,7 @@ blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id) } static grub_err_t -grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), - char **args __attribute__ ((unused))) +blsuki_cmd (grub_extcmd_context_t ctxt, enum blsuki_cmd_type cmd_type) { grub_err_t err; struct grub_arg_list *state = ctxt->state; @@ -1074,24 +1446,50 @@ grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), show_non_default = true; } - err = blsuki_load_entries (path, enable_fallback); + err = blsuki_load_entries (path, enable_fallback, cmd_type); if (err != GRUB_ERR_NONE) return err; - return blsuki_create_entries (show_default, show_non_default, entry_id); + return blsuki_create_entries (show_default, show_non_default, entry_id, cmd_type); +} + +static grub_err_t +grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + return blsuki_cmd (ctxt, BLSUKI_BLS_CMD); } static grub_extcmd_t bls_cmd; +#ifdef GRUB_MACHINE_EFI +static grub_err_t +grub_cmd_uki (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + return blsuki_cmd (ctxt, BLSUKI_UKI_CMD); +} + +static grub_extcmd_t uki_cmd; +#endif + GRUB_MOD_INIT(blsuki) { bls_cmd = grub_register_extcmd ("blscfg", grub_cmd_blscfg, 0, N_("[-p|--path] [-f|--enable-fallback] DIR [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"), N_("Import Boot Loader Specification snippets."), bls_opt); +#ifdef GRUB_MACHINE_EFI + uki_cmd = grub_register_extcmd ("uki", grub_cmd_uki, 0, + N_("[-p|--path] DIR [-f|--enable-fallback] [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"), + N_("Import Unified Kernel Images"), uki_opt); +#endif } GRUB_MOD_FINI(blsuki) { grub_unregister_extcmd (bls_cmd); +#ifdef GRUB_MACHINE_EFI + grub_unregister_extcmd (uki_cmd); +#endif } diff --git a/include/grub/menu.h b/include/grub/menu.h index 9bbe7ab0f..8d06e8400 100644 --- a/include/grub/menu.h +++ b/include/grub/menu.h @@ -28,6 +28,8 @@ struct grub_blsuki_entry grub_size_t keyvals_size; int nkeyvals; char *filename; + char *dirname; + char *devid; bool visible; }; typedef struct grub_blsuki_entry grub_blsuki_entry_t; From de72f3998573f9a607bbba87f0ca1c091d57bad0 Mon Sep 17 00:00:00 2001 From: Mate Kukri Date: Tue, 19 Aug 2025 14:13:10 +0100 Subject: [PATCH 017/208] util/bash-completion.d/Makefile.am: s/mkrescure/mkrescue/g This is a typo that was stopping this bash-completion from being installed. Signed-off-by: Mate Kukri Reviewed-by: Daniel Kiper --- util/bash-completion.d/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/bash-completion.d/Makefile.am b/util/bash-completion.d/Makefile.am index 33fff9546..ede14afcf 100644 --- a/util/bash-completion.d/Makefile.am +++ b/util/bash-completion.d/Makefile.am @@ -50,7 +50,7 @@ CLEANFILES = $(bash_completion_script) \ $(grub_mkfont_script) \ $(grub_mkimage_script) \ $(grub_mkpasswd_pbkdf2_script) \ - $(grub_mkrescure_script) \ + $(grub_mkrescue_script) \ $(grub_probe_script) \ $(grub_reboot_script) \ $(grub_script_check_script) \ @@ -67,7 +67,7 @@ bashcompletion_DATA = $(bash_completion_script) \ $(grub_mkfont_script) \ $(grub_mkimage_script) \ $(grub_mkpasswd_pbkdf2_script) \ - $(grub_mkrescure_script) \ + $(grub_mkrescue_script) \ $(grub_probe_script) \ $(grub_reboot_script) \ $(grub_script_check_script) \ From 49e76ad16f8347959222598d08734788720ee108 Mon Sep 17 00:00:00 2001 From: Kancy Joe Date: Tue, 19 Aug 2025 23:12:03 +0800 Subject: [PATCH 018/208] term/efi/console: Treat key.scan_code 0x0102 (suspend) as Enter Some Qualcomm-based UEFI platforms only provide volume up, volume down, and power keys. The volume keys are already mapped to SCAN_UP and SCAN_DOWN, while the power key is mapped to SCAN_SUSPEND (key.scan_code 0x0102). On such devices, the power key is commonly used as the Enter (confirm) button, since no dedicated Enter key exists. This patch treats key.scan_code 0x0102 as Enter to improve usability on these platforms. Signed-off-by: Kancy Joe Reviewed-by: Daniel Kiper --- grub-core/term/efi/console.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/term/efi/console.c b/grub-core/term/efi/console.c index 258b52737..37c4e5113 100644 --- a/grub-core/term/efi/console.c +++ b/grub-core/term/efi/console.c @@ -219,6 +219,9 @@ grub_efi_translate_key (grub_efi_input_key_t key) else return key.unicode_char; } + /* Some devices use power key (key.scan_code 0x0102, suspend) as Enter. */ + else if (key.scan_code == 0x0102) + return '\r'; /* Some devices send enter with scan_code 0x0d (F3) and unicode_char 0x0d. */ else if (key.scan_code == '\r' && key.unicode_char == '\r') return key.unicode_char; From 7bfb38627bcea85ec7e8400abe822de26f31970b Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Fri, 22 Aug 2025 22:43:17 -0500 Subject: [PATCH 019/208] po: Update translations to build with gettext 0.26 Gettext 0.26 validates format strings. In some cases before the GRUB build process was converting newlines sequences (\n) to (\) which is invalid. Update the impacted language sed script files to ensure newlines use the correct escape sequence. This avoids build errors such as: de@hebrew.po:8192: 'msgstr' is not a valid Shell printf format string, unlike 'msgid'. Reason: This escape sequence is invalid. Fixes: https://savannah.gnu.org/bugs/?67353 Signed-off-by: Andrew Hamilton Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- po/arabic.sed | 2 ++ po/cyrillic.sed | 2 ++ po/greek.sed | 2 ++ po/hebrew.sed | 2 ++ 4 files changed, 8 insertions(+) diff --git a/po/arabic.sed b/po/arabic.sed index 3fbee7248..50bede0f8 100644 --- a/po/arabic.sed +++ b/po/arabic.sed @@ -81,3 +81,5 @@ s,%\([0-9]*\)زو,%\1zu,g s,%\([0-9]*\)كس,%\1x,g s,%\([0-9]*\)لكس,%\1lx,g s,%\([0-9]*\)للكس,%\1llx,g + +s,\\ن,\\n,g diff --git a/po/cyrillic.sed b/po/cyrillic.sed index 472f09529..d3db38838 100644 --- a/po/cyrillic.sed +++ b/po/cyrillic.sed @@ -104,3 +104,5 @@ s,%\([0-9]*\)зу,%\1zu,g s,%\([0-9]*\)ѯ,%\1x,g s,%\([0-9]*\)лѯ,%\1lx,g s,%\([0-9]*\)ллѯ,%\1llx,g + +s,\\н,\\n,g diff --git a/po/greek.sed b/po/greek.sed index 0e81625fb..1ace5fcd2 100644 --- a/po/greek.sed +++ b/po/greek.sed @@ -106,3 +106,5 @@ s,%\([0-9]*\)ζυ,%\1zu,g s,%\([0-9]*\)ξ,%\1x,g s,%\([0-9]*\)λξ,%\1lx,g s,%\([0-9]*\)λλξ,%\1llx,g + +s,\\ν,\\n,g diff --git a/po/hebrew.sed b/po/hebrew.sed index 33174bbdc..ce59e576e 100644 --- a/po/hebrew.sed +++ b/po/hebrew.sed @@ -89,3 +89,5 @@ s,%\([0-9]*\)זוּ,%\1zu,g s,%\([0-9]*\)כּס,%\1x,g s,%\([0-9]*\)לכּס,%\1lx,g s,%\([0-9]*\)ללכּס,%\1llx,g + +s,\\נ,\\n,g From 236663dfbe9b2d5af3e3e386f12c51afc561e2d8 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sat, 23 Aug 2025 18:09:17 -0500 Subject: [PATCH 020/208] INSTALL: Document libtasn1 needed for grub-protect Update INSTALL documentation to note that the optional grub-protect utility requires libtasn1 to build. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- INSTALL | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/INSTALL b/INSTALL index 724584c57..22b68cb6c 100644 --- a/INSTALL +++ b/INSTALL @@ -45,6 +45,10 @@ To build GRUB's graphical terminal (gfxterm), you need: To build grub-mkfont the unicode fonts are required (xfonts-unifont package on Debian). +To build the optional grub-protect utility the following is needed: + +* libtasn1 (libtasn1-6-dev on Debian) + If you use a development snapshot or want to hack on GRUB you may need the following. From cf1b75a144cf3e17c0b1ea31a20d738eea31e2fc Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sat, 23 Aug 2025 18:09:18 -0500 Subject: [PATCH 021/208] BUGS: Update to point to bug tracking system Update the BUGS file to just point to the GRUB bug tracking system. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- BUGS | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/BUGS b/BUGS index 46faa6452..09efa95dd 100644 --- a/BUGS +++ b/BUGS @@ -1,7 +1,3 @@ -GRUB team is aware of following problems: - - Currently search and assembling multidevice abstractions scans - all the devices which can be slow. - - Cache isn't used correctly for video which results in slowness. +Open bugs and issues are captured in the GRUB bug tracking system: -While these are bugs their solution has a potential of breaking more and more -seriously. So it was decided for 1.99 that they aren't fixed. + https://savannah.gnu.org/bugs/?group=grub From 1562dee6921aa8c835df6e7027a840e34c2361e5 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sun, 24 Aug 2025 21:50:11 -0500 Subject: [PATCH 022/208] docs: Clarify section heading and fix wording Update chapter name from "Outline" to "Platform-specific operations" to improve readability. Also slightly improve some wording in this section. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- docs/grub.texi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index 57667e803..048cadfe3 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -9680,10 +9680,10 @@ to install to is specified, UUID is used instead as well. @end multitable @node Platform-specific operations -@chapter Outline +@chapter Platform-specific operations -Some platforms have features which allows to implement -some commands useless or not implementable on others. +Some platforms have features which allow implementation of +certain commands that cannot be implemented on others. Quick summary: From 54c8573ef471928e2789db8e1502cfb1069e4f05 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sun, 24 Aug 2025 21:50:12 -0500 Subject: [PATCH 023/208] docs: Document new libgrypt modules Add documentation for new libgcrypt modules imported into GRUB. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- docs/grub.texi | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/docs/grub.texi b/docs/grub.texi index 048cadfe3..d22c457d8 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -3996,6 +3996,7 @@ Modules can be loaded via the @command{insmod} (@pxref{insmod}) command. * div_test_module:: * dm_nv_module:: * drivemap_module:: +* dsa_sexp_test_module:: * echo_module:: * efi_gop_module:: * efi_uga_module:: @@ -4024,24 +4025,33 @@ Modules can be loaded via the @command{insmod} (@pxref{insmod}) command. * fshelp_module:: * functional_test_module:: * gcry_arcfour_module:: +* gcry_aria_module:: +* gcry_blake2_module:: * gcry_blowfish_module:: * gcry_camellia_module:: * gcry_cast5_module:: * gcry_crc_module:: * gcry_des_module:: * gcry_dsa_module:: +* gcry_gost28147_module:: +* gcry_gostr3411_94_module:: * gcry_idea_module:: +* gcry_keccak_module:: * gcry_md4_module:: * gcry_md5_module:: * gcry_rfc2268_module:: * gcry_rijndael_module:: * gcry_rmd160_module:: * gcry_rsa_module:: +* gcry_salsa20_module:: * gcry_seed_module:: * gcry_serpent_module:: * gcry_sha1_module:: * gcry_sha256_module:: * gcry_sha512_module:: +* gcry_sm3_module:: +* gcry_sm4_module:: +* gcry_stribog_module:: * gcry_tiger_module:: * gcry_twofish_module:: * gcry_whirlpool_module:: @@ -4160,6 +4170,7 @@ Modules can be loaded via the @command{insmod} (@pxref{insmod}) command. * probe_module:: * procfs_module:: * progress_module:: +* pubkey_module:: * pxe_module:: * pxechain_module:: * raid5rec_module:: @@ -4172,6 +4183,7 @@ Modules can be loaded via the @command{insmod} (@pxref{insmod}) command. * reiserfs_module:: * relocator_module:: * romfs_module:: +* rsa_sexp_test_module:: * scsi_module:: * sdl_module:: * search_module:: @@ -4557,6 +4569,10 @@ This module provides support for handling some Nvidia "fakeraid" disk devices. This module provides support for the @command{drivemap} to manage BIOS drive mappings. @xref{drivemap} for more information. +@node dsa_sexp_test_module +@section dsa_sexp_test +This module provides a test of the libgcrypt DSA functionality in GRUB. + @node echo_module @section echo This module provides support for the @command{echo} to display a line of text. @@ -4694,6 +4710,11 @@ This module provides support functions (helper functions) for file systems. This module provides support for running the GRUB functional tests using commands @command{functional_test} and @command{all_functional_test}. +@node gcry_aria_module +@section gcry_aria +This module provides support for the ARIA cipher. +This GRUB module is based on libgcrypt. + @node gcry_arcfour_module @section gcry_arcfour This module provides support for the arcfour stream cipher also known as RC4. @@ -4701,6 +4722,11 @@ If security is a concern, RC4 / arcfour cipher is consider broken (multiple known vulnerabilities make this insecure). This GRUB module is based on libgcrypt. +@node gcry_blake2_module +@section gcry_blake2 +This module provides support for the BLAKE2b and BLAKE2s message digests. +This GRUB module is based on libgcrypt. + @node gcry_blowfish_module @section gcry_blowfish This module provides support for the Blowfish cipher. @@ -4735,12 +4761,28 @@ This GRUB module is based on libgcrypt. This module provides support for the Digital Signature Algorithm (DSA) cipher. This GRUB module is based on libgcrypt. +@node gcry_gost28147_module +@section gcry_gost28147 +This module provides support for the GOST 28147-89 cipher. +This GRUB module is based on libgcrypt. + +@node gcry_gostr3411_94_module +@section gcry_gostr3411_94 +This module provides support for the GOST R 34.11-94 message digest. +This GRUB module is based on libgcrypt. + @node gcry_idea_module @section gcry_idea This module provides support for the International Data Encryption Algorithm (IDEA) cipher. This GRUB module is based on libgcrypt. +@node gcry_keccak_module +@section gcry_keccak +This module provides support for the SHA3 hash message digests (including +SHAKE128 and SHAKE256). +This GRUB module is based on libgcrypt. + @node gcry_md4_module @section gcry_md4 This module provides support for the Message Digest 4 (MD4) message digest. @@ -4775,6 +4817,11 @@ This GRUB module is based on libgcrypt. This module provides support for the Rivest–Shamir–Adleman (RSA) cipher. This GRUB module is based on libgcrypt. +@node gcry_salsa20_module +@section gcry_salsa20 +This module provides support for the Salsa20 cipher. +This GRUB module is based on libgcrypt. + @node gcry_seed_module @section gcry_seed This module provides support for the SEED cipher. @@ -4805,6 +4852,21 @@ This module provides support for the Secure Hash Algorithm 2 (384 and 512 bit) (SHA-384 / SHA-512) message digests. This GRUB module is based on libgcrypt. +@node gcry_sm3_module +@section gcry_sm3 +This module provides support for the SM3 message digest. +This GRUB module is based on libgcrypt. + +@node gcry_sm4_module +@section gcry_sm4 +This module provides support for the SM4 cipher. +This GRUB module is based on libgcrypt. + +@node gcry_stribog_module +@section gcry_stribog +This module provides support for the GOST R 34.11-2012 (Stribog) message digest. +This GRUB module is based on libgcrypt. + @node gcry_tiger_module @section gcry_tiger This module provides support for the Tiger, Tiger 1, and Tiger 2 message @@ -5570,6 +5632,11 @@ like interface to some GRUB internal data. @section progress This module provides support for showing file loading progress to the terminal. +@node pubkey_module +@section pubkey +This module provides supporting functions for using RSA and DSA public keys. +This GRUB module is based on libgcrypt. + @node pxe_module @section pxe This module provides support for Preboot Execution Environment (PXE) network @@ -5632,6 +5699,10 @@ This module provides support for the Read-Only Memory File System (ROMFS). Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more information. +@node rsa_sexp_test_module +@section rsa_sexp_test +This module provides a test of the libgcrypt RSA functionality in GRUB. + @node scsi_module @section scsi This module provides support for the Small Computer System Interface (SCSI) From 733cc28eb60f9f6b807416c1aa500c86601ac575 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sun, 24 Aug 2025 21:50:13 -0500 Subject: [PATCH 024/208] docs: Update Future section to reflect current release Update the Future section of the GRUB manual to reflect current work on the 2.x series. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- docs/grub.texi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/grub.texi b/docs/grub.texi index d22c457d8..130968634 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -10793,7 +10793,7 @@ Once we get your report, we will try to fix the bugs. @appendix Where GRUB will go GRUB 2 is now quite stable and used in many production systems. We are -currently working towards a 2.0 release. +currently working on the 2.x series. If you are interested in the development of GRUB 2, take a look at @uref{http://www.gnu.org/software/grub/grub.html, the homepage}. From 4e42199f35005979c7c9773828d2051941a605f4 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sun, 24 Aug 2025 21:50:14 -0500 Subject: [PATCH 025/208] docs: Correct some URLs Correct some outdated links to various websites and change http to https in a few places. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- docs/grub.texi | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index 130968634..b81eb1d93 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -10688,16 +10688,16 @@ Print each line of input after reading it. @strong{Caution:} GRUB requires binutils-2.9.1.0.23 or later because the GNU assembler has been changed so that it can produce real 16bits machine code between 2.9.1 and 2.9.1.0.x. See -@uref{http://sources.redhat.com/binutils/}, to obtain information on +@uref{https://www.gnu.org/software/binutils/}, to obtain information on how to get the latest version. @end quotation GRUB is available from the GNU alpha archive site -@uref{ftp://ftp.gnu.org/gnu/grub} or any of its mirrors. The file +@uref{https://ftp.gnu.org/gnu/grub/} or any of its mirrors. The file will be named grub-version.tar.gz. The current version is @value{VERSION}, so the file you should grab is: -@uref{ftp://ftp.gnu.org/gnu/grub/grub-@value{VERSION}.tar.gz} +@uref{https://ftp.gnu.org/gnu/grub/grub-@value{VERSION}.tar.gz} To unbundle GRUB use the instruction: @@ -10719,7 +10719,7 @@ just do: @end example Also, the latest version is available using Git. See -@uref{http://www.gnu.org/software/grub/grub-download.html} for more +@uref{https://www.gnu.org/software/grub/grub-download.html} for more information. @node Reporting bugs @@ -10731,7 +10731,7 @@ list below before you submit bugs: @enumerate @item Before getting unsettled, read this manual through and through. Also, -see the @uref{http://www.gnu.org/software/grub/grub-faq.html, GNU GRUB FAQ}. +see the @uref{https://www.gnu.org/software/grub/grub-faq.html, GNU GRUB FAQ}. @item Always mention the information on your GRUB. The version number and the @@ -10781,7 +10781,7 @@ important. @end enumerate If you follow the guideline above, submit a report to the -@uref{http://savannah.gnu.org/bugs/?group=grub, Bug Tracking System}. +@uref{https://savannah.gnu.org/bugs/?group=grub, Bug Tracking System}. Alternatively, you can submit a report via electronic mail to @email{bug-grub@@gnu.org}, but we strongly recommend that you use the Bug Tracking System, because e-mail can be passed over easily. @@ -10796,7 +10796,7 @@ GRUB 2 is now quite stable and used in many production systems. We are currently working on the 2.x series. If you are interested in the development of GRUB 2, take a look at -@uref{http://www.gnu.org/software/grub/grub.html, the homepage}. +@uref{https://www.gnu.org/software/grub/grub.html, the homepage}. From 02788bfdf95e885c7a7b0f4155b2910f1f1ec62f Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sun, 24 Aug 2025 20:38:41 -0500 Subject: [PATCH 026/208] bootstrap: Ensure shallow gnulib clone works on newer git Update the bootstrap script to be compatible with newer versions of git that changed the "git clone -h" output from containing: --depth to: --[no-]depth This bootstrap script is pulled the latest gnulib version from gnulib git, commit 9a1a6385 (Silence 'time-stamp' warnings with bleeding-edge Emacs.). This change avoids a full clone on gnulib, saving something like 50 MB. Fixes: https://savannah.gnu.org/bugs/?66357 Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- bootstrap | 1990 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 1251 insertions(+), 739 deletions(-) diff --git a/bootstrap b/bootstrap index dc2238f4a..dc9fb4383 100755 --- a/bootstrap +++ b/bootstrap @@ -1,34 +1,67 @@ #! /bin/sh -# Print a version string. -scriptversion=2022-01-26.05; # UTC +# DO NOT EDIT! GENERATED AUTOMATICALLY! # Bootstrap this package from checked-out sources. -# Copyright (C) 2003-2022 Free Software Foundation, Inc. +scriptversion=2025-06-10.02; # UTC +# Copyright (C) 2003-2025 Free Software Foundation, Inc. +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. - +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. - +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Originally written by Paul Eggert. The canonical version of this -# script is maintained as build-aux/bootstrap in gnulib, however, to -# be useful to your project, you should place a copy of it under -# version control in the top-level directory of your project. The +# script is maintained as top/bootstrap in gnulib. However, to be +# useful to your package, you should place a copy of it under version +# control in the top-level directory of your package. The intent is +# that all customization can be done with a bootstrap.conf file also +# maintained in your version control; gnulib comes with a template +# build-aux/bootstrap.conf to get you started. + +# Please report bugs or propose patches to bug-gnulib@gnu.org. + +me="$0" +medir=`dirname "$me"` + +# Read the function library and the configuration. + +# A library of shell functions for autopull.sh, autogen.sh, and bootstrap. + +scriptlibversion=2025-06-10.02; # UTC + +# Copyright (C) 2003-2025 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Originally written by Paul Eggert. The canonical version of this +# script is maintained as top/bootstrap-funclib.sh in gnulib. However, +# to be useful to your package, you should place a copy of it under +# version control in the top-level directory of your package. The # intent is that all customization can be done with a bootstrap.conf # file also maintained in your version control; gnulib comes with a # template build-aux/bootstrap.conf to get you started. -# Please report bugs or propose patches to bug-gnulib@gnu.org. - nl=' ' @@ -36,86 +69,13 @@ nl=' LC_ALL=C export LC_ALL -# Ensure that CDPATH is not set. Otherwise, the output from cd -# would cause trouble in at least one use below. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -local_gl_dir=gl - # Honor $PERL, but work even if there is none. PERL="${PERL-perl}" -me=$0 - default_gnulib_url=https://git.savannah.gnu.org/git/gnulib.git -usage() { - cat </dev/null) +normalize_package_name=' + s/^GNU // + y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ + s/[^abcdefghijklmnopqrstuvwxyz0123456789_]/-/g +' +package=$(${AUTOCONF:-autoconf} --trace 'AC_INIT:$4' configure.ac 2>/dev/null) if test -z "$package"; then package=$(sed -n "$extract_package_name" configure.ac) \ || die 'cannot find package name in configure.ac' fi +package=$(echo "$package" | sed "$normalize_package_name") gnulib_name=lib$package build_aux=build-aux @@ -247,15 +214,13 @@ COPYRIGHT_HOLDER='Free Software Foundation, Inc.' MSGID_BUGS_ADDRESS=bug-$package@gnu.org # Files we don't want to import. +# XXX Not used. excluded_files= # File that should exist in the top directory of a checked out hierarchy, # but not in a distribution tarball. checkout_only_file=README-hacking -# Whether to use copies instead of symlinks. -copy=false - # Set this to '.cvsignore .gitignore' in bootstrap.conf if you want # those files to be generated in directories like lib/, m4/, and po/. # Or set it to 'auto' to make this script select which to use based @@ -266,8 +231,13 @@ vc_ignore=auto # default. bootstrap_sync=false -# Use git to update gnulib sources -use_git=true +# Override the default configuration, if necessary. +# Make sure that bootstrap.conf is sourced from the current directory +# if we were invoked as "sh bootstrap". +conffile=`dirname "$me"`/bootstrap.conf +test -r "$conffile" && . "$conffile" + +# ------------------------- Build-time prerequisites ------------------------- check_exists() { if test "$1" = "--verbose"; then @@ -284,6 +254,202 @@ check_exists() { test $? -lt 126 } +# Note this deviates from the version comparison in automake +# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a +# but this should suffice as we won't be specifying old +# version formats or redundant trailing .0 in bootstrap.conf. +# If we did want full compatibility then we should probably +# use m4_version_compare from autoconf. +sort_ver() { # sort -V is not generally available + ver1="$1" + ver2="$2" + + # split on '.' and compare each component + i=1 + while : ; do + p1=$(echo "$ver1" | cut -d. -f$i) + p2=$(echo "$ver2" | cut -d. -f$i) + if [ ! "$p1" ]; then + echo "$1 $2" + break + elif [ ! "$p2" ]; then + echo "$2 $1" + break + elif [ ! "$p1" = "$p2" ]; then + if [ "$p1" -gt "$p2" ] 2>/dev/null; then # numeric comparison + echo "$2 $1" + elif [ "$p2" -gt "$p1" ] 2>/dev/null; then # numeric comparison + echo "$1 $2" + else # numeric, then lexicographic comparison + lp=$(printf "%s\n%s\n" "$p1" "$p2" | LANG=C sort -n | tail -n1) + if [ "$lp" = "$p2" ]; then + echo "$1 $2" + else + echo "$2 $1" + fi + fi + break + fi + i=$(($i+1)) + done +} + +get_version_sed=' +# Move version to start of line. +s/.*[v ]\([0-9]\)/\1/ + +# Skip lines that do not start with version. +/^[0-9]/!d + +# Remove characters after the version. +s/[^.a-z0-9-].*// + +# The first component must be digits only. +s/^\([0-9]*\)[a-z-].*/\1/ + +#the following essentially does s/5.005/5.5/ +s/\.0*\([1-9]\)/.\1/g +p +q' + +get_version() { + app=$1 + + $app --version >/dev/null 2>&1 || { $app --version; return 1; } + + $app --version 2>&1 | sed -n "$get_version_sed" +} + +check_versions() { + ret=0 + + while read app req_ver; do + # We only need libtoolize from the libtool package. + if test "$app" = libtool; then + app=libtoolize + fi + # Exempt git if git is not needed. + if test "$app" = git; then + $check_git || continue + fi + # Honor $APP variables ($TAR, $AUTOCONF, etc.) + appvar=$(echo $app | LC_ALL=C tr '[a-z]-' '[A-Z]_') + test "$appvar" = TAR && appvar=AMTAR + case $appvar in + GZIP) ;; # Do not use $GZIP: it contains gzip options. + PERL::*) ;; # Keep perl modules as-is + *) eval "app=\${$appvar-$app}" ;; + esac + + # Handle the still-experimental Automake-NG programs specially. + # They remain named as the mainstream Automake programs ("automake", + # and "aclocal") to avoid gratuitous incompatibilities with + # preexisting usages (by, say, autoreconf, or custom autogen.sh + # scripts), but correctly identify themselves (as being part of + # "GNU automake-ng") when asked their version. + case $app in + automake-ng|aclocal-ng) + app=${app%-ng} + ($app --version | grep '(GNU automake-ng)') >/dev/null 2>&1 || { + warn_ "Error: '$app' not found or not from Automake-NG" + ret=1 + continue + } ;; + # Another check is for perl modules. These can be written as + # e.g. perl::XML::XPath in case of XML::XPath module, etc. + perl::*) + # Extract module name + app="${app#perl::}" + if ! $PERL -m"$app" -e 'exit 0' >/dev/null 2>&1; then + warn_ "Error: perl module '$app' not found" + ret=1 + fi + continue + ;; + esac + if [ "$req_ver" = "-" ]; then + # Merely require app to exist; not all prereq apps are well-behaved + # so we have to rely on $? rather than get_version. + if ! check_exists --verbose $app; then + warn_ "Error: '$app' not found" + ret=1 + fi + else + # Require app to produce a new enough version string. + inst_ver=$(get_version $app) + if [ ! "$inst_ver" ]; then + warn_ "Error: '$app' not found" + ret=1 + else + latest_ver=$(sort_ver $req_ver $inst_ver | cut -d' ' -f2) + if [ ! "$latest_ver" = "$inst_ver" ]; then + warnf_ '%s\n' \ + "Error: '$app' version == $inst_ver is too old" \ + " '$app' version >= $req_ver is required" + ret=1 + fi + fi + fi + done + + return $ret +} + +print_versions() { + echo "Program Min_version" + echo "----------------------" + printf %s "$buildreq" + echo "----------------------" + # can't depend on column -t +} + +# check_build_prerequisites check_git +check_build_prerequisites() +{ + check_git="$1" + + # gnulib-tool requires at least automake and autoconf. + # If either is not listed, add it (with minimum version) as a prerequisite. + case $buildreq in + *automake*) ;; + *) buildreq="automake 1.9 +$buildreq" ;; + esac + case $buildreq in + *autoconf*) ;; + *) buildreq="autoconf 2.59 +$buildreq" ;; + esac + + # When we can deduce that gnulib-tool will require patch, + # and when patch is not already listed as a prerequisite, add it, too. + if test -d "$local_gl_dir" \ + && ! find "$local_gl_dir" -name '*.diff' -exec false {} +; then + case $buildreq in + *patch*) ;; + *) buildreq="patch - +$buildreq" ;; + esac + fi + + if ! printf '%s' "$buildreq" | check_versions; then + echo >&2 + if test -f README-prereq; then + die "See README-prereq for how to get the prerequisite programs" + else + die "Please install the prerequisite programs" + fi + fi + + # Warn the user if autom4te appears to be broken; this causes known + # issues with at least gettext 0.18.3. + probe=$(echo 'm4_quote([hi])' | autom4te -l M4sugar -t 'm4_quote:$%' -) + if test "x$probe" != xhi; then + warn_ "WARNING: your autom4te wrapper eats stdin;" + warn_ "if bootstrap fails, consider upgrading your autotools" + fi +} + # find_tool ENVVAR NAMES... # ------------------------- # Search for a required program. Use the value of ENVVAR, if set, @@ -313,6 +479,530 @@ find_tool () eval "export $find_tool_envvar" } +# --------------------- Preparing GNULIB_SRCDIR for use. --------------------- +# This is part of autopull.sh, but bootstrap needs it too, for self-upgrading. + +# cleanup_gnulib fails, removing the directory $gnulib_path first. +cleanup_gnulib() { + status=$? + rm -fr "$gnulib_path" + exit $status +} + +git_modules_config () { + test -f .gitmodules && git config --file .gitmodules "$@" +} + +prepare_GNULIB_SRCDIR () +{ + if test -n "$GNULIB_SRCDIR"; then + # Use GNULIB_SRCDIR directly. + # We already checked that $GNULIB_SRCDIR references a directory. + # Verify that it contains a gnulib checkout. + test -f "$GNULIB_SRCDIR/gnulib-tool" \ + || die "Error: --gnulib-srcdir or \$GNULIB_SRCDIR is specified," \ + "but does not contain gnulib-tool" + if test -n "$GNULIB_REVISION" && $use_git; then + # The 'git checkout "$GNULIB_REVISION"' command succeeds if the + # GNULIB_REVISION is a commit hash that exists locally, or if it is + # branch name that can be fetched from origin. It fails, however, + # if the GNULIB_REVISION is a commit hash that only exists in + # origin. In this case, we need a 'git fetch' and then retry + # 'git checkout "$GNULIB_REVISION"'. + git -C "$GNULIB_SRCDIR" checkout "$GNULIB_REVISION" 2>/dev/null \ + || { git -C "$GNULIB_SRCDIR" fetch origin \ + && git -C "$GNULIB_SRCDIR" checkout "$GNULIB_REVISION"; } \ + || exit $? + fi + else + if ! $use_git; then + die "Error: --no-git is specified," \ + "but neither --gnulib-srcdir nor \$GNULIB_SRCDIR is specified" + fi + if git submodule -h | grep -- --reference > /dev/null; then + : + else + die "git version is too old, git >= 1.6.4 is required" + fi + gnulib_path=$(git_modules_config submodule.gnulib.path) + if test -n "$gnulib_path"; then + # A submodule 'gnulib' is configured. + # Get gnulib files. Populate $gnulib_path, updating the submodule. + if test -n "$GNULIB_REFDIR" && test -d "$GNULIB_REFDIR"/.git; then + # Use GNULIB_REFDIR as a reference. + echo "$0: getting gnulib files..." + git submodule update --init --reference "$GNULIB_REFDIR" "$gnulib_path"\ + || exit $? + else + # GNULIB_REFDIR is not set or not usable. Ignore it. + if git_modules_config submodule.gnulib.url >/dev/null; then + echo "$0: getting gnulib files..." + git submodule init -- "$gnulib_path" || exit $? + git submodule update -- "$gnulib_path" || exit $? + else + die "Error: submodule 'gnulib' has no configured url" + fi + fi + else + gnulib_path='gnulib' + if test ! -d "$gnulib_path"; then + # The subdirectory 'gnulib' does not yet exist. Clone into it. + echo "$0: getting gnulib files..." + trap cleanup_gnulib HUP INT PIPE TERM + gnulib_url=${GNULIB_URL:-$default_gnulib_url} + if test -n "$GNULIB_REFDIR" && test -d "$GNULIB_REFDIR"/.git; then + # Use GNULIB_REFDIR as a reference. + git clone "$GNULIB_REFDIR" "$gnulib_path" \ + && git -C "$gnulib_path" remote set-url origin "$gnulib_url" \ + && if test -z "$GNULIB_REVISION"; then + git -C "$gnulib_path" pull origin \ + && { + # We want the default branch of "$gnulib_url" (since that's + # the behaviour if GNULIB_REFDIR is not specified), not the + # current branch of "$GNULIB_REFDIR". + default_branch=`LC_ALL=C git -C "$gnulib_path" \ + remote show origin \ + | sed -n -e 's/^ *HEAD branch: //p'` + test -n "$default_branch" || default_branch='master' + git -C "$gnulib_path" checkout "$default_branch" + } + else + # The 'git checkout "$GNULIB_REVISION"' command succeeds if the + # GNULIB_REVISION is a commit hash that exists locally, or if it + # is a branch name that can be fetched from origin. It fails, + # however, if the GNULIB_REVISION is a commit hash that only + # exists in origin. In this case, we need a 'git fetch' and then + # retry 'git checkout "$GNULIB_REVISION"'. + git -C "$gnulib_path" checkout "$GNULIB_REVISION" 2>/dev/null \ + || { git -C "$gnulib_path" fetch origin \ + && git -C "$gnulib_path" checkout "$GNULIB_REVISION"; } + fi \ + || cleanup_gnulib + else + # GNULIB_REFDIR is not set or not usable. Ignore it. + shallow='--depth 2' + if test -z "$GNULIB_REVISION"; then + git clone $shallow "$gnulib_url" "$gnulib_path" \ + || cleanup_gnulib + else + # Only want a shallow checkout of $GNULIB_REVISION, but git does not + # support cloning by commit hash. So attempt a shallow fetch by + # commit hash to minimize the amount of data downloaded and changes + # needed to be processed, which can drastically reduce download and + # processing time for checkout. If the fetch by commit fails, a + # shallow fetch cannot be performed because we do not know what the + # depth of the commit is without fetching all commits. So fall back + # to fetching all commits. + # $GNULIB_REVISION can be a commit id, a tag name, or a branch name. + mkdir -p "$gnulib_path" + # Use a -c option to silence an annoying message + # "hint: Using 'master' as the name for the initial branch." + # (cf. ). + git -C "$gnulib_path" -c init.defaultBranch=master init + git -C "$gnulib_path" remote add origin "$gnulib_url" + if git -C "$gnulib_path" fetch $shallow origin "$GNULIB_REVISION" + then + # "git fetch" of the specific commit succeeded. + git -C "$gnulib_path" reset --hard FETCH_HEAD \ + || cleanup_gnulib + # "git fetch" does not fetch tags (at least in git version 2.43). + # If $GNULIB_REVISION is a tag (not a commit id or branch name), + # add the tag explicitly. + revision=`git -C "$gnulib_path" log -1 --pretty=format:%H` + branch=`LC_ALL=C git -C "$gnulib_path" remote show origin \ + | sed -n -e 's/^ \([^ ]*\) * tracked$/\1/p'` + test "$revision" = "$GNULIB_REVISION" \ + || test "$branch" = "$GNULIB_REVISION" \ + || git -C "$gnulib_path" tag "$GNULIB_REVISION" + else + # Fetch the entire repository. + git -C "$gnulib_path" fetch origin \ + || cleanup_gnulib + git -C "$gnulib_path" checkout "$GNULIB_REVISION" \ + || cleanup_gnulib + fi + fi + fi + trap - HUP INT PIPE TERM + else + # The subdirectory 'gnulib' already exists. + if test -n "$GNULIB_REVISION"; then + if test -d "$gnulib_path/.git"; then + # The 'git checkout "$GNULIB_REVISION"' command succeeds if the + # GNULIB_REVISION is a commit hash that exists locally, or if it + # is a branch name that can be fetched from origin. It fails, + # however, if the GNULIB_REVISION is a commit hash that only + # exists in origin. In this case, we need a 'git fetch' and then + # retry 'git checkout "$GNULIB_REVISION"'. + git -C "$gnulib_path" checkout "$GNULIB_REVISION" 2>/dev/null \ + || { git -C "$gnulib_path" fetch origin \ + && git -C "$gnulib_path" checkout "$GNULIB_REVISION"; } \ + || exit $? + else + die "Error: GNULIB_REVISION is specified in bootstrap.conf," \ + "but '$gnulib_path' contains no git history" + fi + fi + fi + fi + # Verify that $gnulib_path contains a gnulib checkout. + test -f "$gnulib_path/gnulib-tool" \ + || die "Error: '$gnulib_path' is supposed to contain a gnulib checkout," \ + "but does not contain gnulib-tool" + GNULIB_SRCDIR=$gnulib_path + fi + # $GNULIB_SRCDIR now points to the version of gnulib to use, and + # we no longer need to use git or $gnulib_path below here. +} + +# -------- Upgrading bootstrap to the version found in GNULIB_SRCDIR. -------- + +upgrade_bootstrap () +{ + if test -f "$medir"/bootstrap-funclib.sh; then + update_lib=true + { cmp -s "$medir"/bootstrap "$GNULIB_SRCDIR/top/bootstrap" \ + && cmp -s "$medir"/bootstrap-funclib.sh \ + "$GNULIB_SRCDIR/top/bootstrap-funclib.sh" \ + && cmp -s "$medir"/autopull.sh "$GNULIB_SRCDIR/top/autopull.sh" \ + && cmp -s "$medir"/autogen.sh "$GNULIB_SRCDIR/top/autogen.sh"; \ + } + else + update_lib=false + cmp -s "$medir"/bootstrap "$GNULIB_SRCDIR/build-aux/bootstrap" + fi || { + if $update_lib; then + echo "$0: updating bootstrap & companions and restarting..." + else + echo "$0: updating bootstrap and restarting..." + fi + case $(sh -c 'echo "$1"' -- a) in + a) ignored=--;; + *) ignored=ignored;; + esac + u=$update_lib + exec sh -c \ + '{ if '$u' && test -f "$1"; then cp "$1" "$3"; else cp "$2" "$3"; fi; } && + { if '$u' && test -f "$4"; then cp "$4" "$5"; else rm -f "$5"; fi; } && + { if '$u' && test -f "$6"; then cp "$6" "$7"; else rm -f "$7"; fi; } && + { if '$u' && test -f "$8"; then cp "$8" "$9"; else rm -f "$9"; fi; } && + shift && shift && shift && shift && shift && + shift && shift && shift && shift && + exec "${CONFIG_SHELL-/bin/sh}" "$@"' \ + $ignored \ + "$GNULIB_SRCDIR/top/bootstrap" "$GNULIB_SRCDIR/build-aux/bootstrap" \ + "$medir/bootstrap" \ + "$GNULIB_SRCDIR/top/bootstrap-funclib.sh" "$medir/bootstrap-funclib.sh" \ + "$GNULIB_SRCDIR/top/autopull.sh" "$medir/autopull.sh" \ + "$GNULIB_SRCDIR/top/autogen.sh" "$medir/autogen.sh" \ + "$0" "$@" --no-bootstrap-sync + } +} + +# ---------------------------------------------------------------------------- + +if test x"$gnulib_modules$gnulib_files$gnulib_extra_files" = x; then + use_gnulib=false +else + use_gnulib=true +fi + +# -------- Fetch auxiliary files from the network. -------------------------- + +autopull_usage() { + cat </dev/null 2>&1 && unset CDPATH + + # Parse options. + + # Use git to update gnulib sources + use_git=true + + for option + do + case $option in + --help) + autopull_usage + return;; + --version) + set -e + echo "autopull.sh $scriptlibversion" + echo "$copyright" + return 0 + ;; + --skip-po) + SKIP_PO=t;; + --force) + checkout_only_file=;; + --bootstrap-sync) + bootstrap_sync=true;; + --no-bootstrap-sync) + bootstrap_sync=false;; + --no-git) + use_git=false;; + *) + bootstrap_option_hook $option || die "$option: unknown option";; + esac + done + + $use_git || test -n "$GNULIB_SRCDIR" \ + || die "Error: --no-git requires \$GNULIB_SRCDIR environment variable" \ + "or --gnulib-srcdir option" + test -z "$GNULIB_SRCDIR" || test -d "$GNULIB_SRCDIR" \ + || die "Error: \$GNULIB_SRCDIR environment variable" \ + "or --gnulib-srcdir option is specified," \ + "but does not denote a directory" + + if test -n "$checkout_only_file" && test ! -r "$checkout_only_file"; then + die "Running this script from a non-checked-out distribution is risky." + fi + + check_build_prerequisites $use_git + + if $use_gnulib || $bootstrap_sync; then + prepare_GNULIB_SRCDIR + if $bootstrap_sync; then + upgrade_bootstrap "$@" + fi + fi + + # Find sha1sum, named gsha1sum on MacPorts, shasum on Mac OS X 10.6. + # Also find the compatible sha1 utility on the BSDs + if test x"$SKIP_PO" = x; then + find_tool SHA1SUM sha1sum gsha1sum shasum sha1 + fi + + # See if we can use gnulib's git-merge-changelog merge driver. + if $use_git && test -d .git && check_exists git; then + if git config merge.merge-changelog.driver >/dev/null ; then + : + elif check_exists git-merge-changelog; then + echo "$0: initializing git-merge-changelog driver" + git config merge.merge-changelog.name 'GNU-style ChangeLog merge driver' + git config merge.merge-changelog.driver 'git-merge-changelog %O %A %B' + else + echo "$0: consider installing git-merge-changelog from gnulib" + fi + fi + + case $SKIP_PO in + '') + if test -d po; then + update_po_files po $package || return + fi + + if test -d runtime-po; then + update_po_files runtime-po $package-runtime || return + fi;; + esac + + # --------------------------------------------------------------------------- + + bootstrap_post_pull_hook \ + || die "bootstrap_post_pull_hook failed" + + # Don't proceed if there are uninitialized submodules. In particular, + # autogen.sh will remove dangling links, which might be links into + # uninitialized submodules. + # But it's OK if the 'gnulib' submodule is uninitialized, as long as + # GNULIB_SRCDIR is set. + if $use_git; then + # Uninitialized submodules are listed with an initial dash. + uninitialized=`git submodule | grep '^-' | awk '{ print $2 }'` + if test -n "$GNULIB_SRCDIR"; then + uninitialized=`echo "$uninitialized" | grep -v '^gnulib$'` + fi + if test -n "$uninitialized"; then + uninit_comma=`echo "$uninitialized" | tr '\n' ',' | sed -e 's|,$|.|'` + die "Some git submodules are not initialized: "$uninit_comma \ + "Either use option '--no-git'," \ + "or run 'git submodule update --init' and bootstrap again." + fi + fi + + if test -f "$medir"/autogen.sh; then + echo "$0: done. Now you can run '$medir/autogen.sh'." + fi +} + +# ----------------------------- Get translations. ----------------------------- + +download_po_files() { + subdir=$1 + domain=$2 + echo "$me: getting translations into $subdir for $domain..." + cmd=$(printf "$po_download_command_format" "$subdir" "$domain") + eval "$cmd" +} + +# Mirror .po files to $po_dir/.reference and copy only the new +# or modified ones into $po_dir. Also update $po_dir/LINGUAS. +# Note po files that exist locally only are left in $po_dir but will +# not be included in LINGUAS and hence will not be distributed. +update_po_files() { + # Directory containing primary .po files. + # Overwrite them only when we're sure a .po file is new. + po_dir=$1 + domain=$2 + + # Mirror *.po files into this dir. + # Usually contains *.s1 checksum files. + ref_po_dir="$po_dir/.reference" + + test -d $ref_po_dir || mkdir $ref_po_dir || return + download_po_files $ref_po_dir $domain \ + && ls "$ref_po_dir"/*.po 2>/dev/null | + sed 's|.*/||; s|\.po$||' > "$po_dir/LINGUAS" || return + + for po in x $(ls $ref_po_dir | sed -n 's/\.po$//p'); do + case $po in x) continue;; esac + new_po="$ref_po_dir/$po.po" + cksum_file="$ref_po_dir/$po.s1" + if ! test -f "$cksum_file" || + ! test -f "$po_dir/$po.po" || + ! $SHA1SUM -c "$cksum_file" < "$new_po" > /dev/null 2>&1; then + echo "$me: updated $po_dir/$po.po..." + cp "$new_po" "$po_dir/$po.po" \ + && $SHA1SUM < "$new_po" > "$cksum_file" || return + fi + done +} + +# -------- Generate files automatically from existing sources. -------------- + +autogen_usage() { + cat < /dev/null 2>&1 + elif test -d .svn; then + svn log -r HEAD "$file" > /dev/null 2>&1 + elif test -d CVS; then + grep -F "/${file##*/}/" "$parent/CVS/Entries" 2>/dev/null | + grep '^/[^/]*/[0-9]' > /dev/null + else + warn_ "no version control for $file?" + false + fi +} + # Strip blank and comment lines to leave significant entries. gitignore_entries() { sed '/^#/d; /^$/d' "$@" @@ -374,7 +1064,7 @@ symlink_to_dir() for dot_ig in x $vc_ignore; do test $dot_ig = x && continue ig=$parent/$dot_ig - insert_vc_ignore $ig "${dst_dir##*/}" + insert_vc_ignore $ig "${dst_dir##*/}/" done fi @@ -423,30 +1113,426 @@ symlink_to_dir() } } -# Override the default configuration, if necessary. -# Make sure that bootstrap.conf is sourced from the current directory -# if we were invoked as "sh bootstrap". -case "$0" in - */*) test -r "$0.conf" && . "$0.conf" ;; - *) test -r "$0.conf" && . ./"$0.conf" ;; -esac +# Regenerate all autogeneratable files that are omitted from the +# version control repository. In particular, regenerate all +# aclocal.m4, config.h.in, Makefile.in, configure files with new +# versions of autoconf or automake. +autogen() +{ + # Ensure that CDPATH is not set. Otherwise, the output from cd + # would cause trouble in at least one use below. + (unset CDPATH) >/dev/null 2>&1 && unset CDPATH -if test "$vc_ignore" = auto; then - vc_ignore= - test -d .git && vc_ignore=.gitignore - test -d CVS && vc_ignore="$vc_ignore .cvsignore" -fi + # Environment variables that may be set by the user. + : "${AUTOPOINT=autopoint}" + : "${AUTORECONF=autoreconf}" -if test x"$gnulib_modules$gnulib_files$gnulib_extra_files" = x; then - use_gnulib=false -else - use_gnulib=true -fi + if test "$vc_ignore" = auto; then + vc_ignore= + test -d .git && vc_ignore=.gitignore + test -d CVS && vc_ignore="$vc_ignore .cvsignore" + fi -# Translate configuration into internal form. + + # Parse options. + + # Whether to use copies instead of symlinks. + copy=false + + for option + do + case $option in + --help) + autogen_usage + return;; + --version) + set -e + echo "autogen.sh $scriptlibversion" + echo "$copyright" + return 0 + ;; + --force) + checkout_only_file=;; + --copy) + copy=true;; + *) + bootstrap_option_hook $option || die "$option: unknown option";; + esac + done + + test -z "$GNULIB_SRCDIR" || test -d "$GNULIB_SRCDIR" \ + || die "Error: \$GNULIB_SRCDIR environment variable or --gnulib-srcdir" \ + "option is specified, but does not denote a directory" + + if test -n "$checkout_only_file" && test ! -r "$checkout_only_file"; then + die "Running this script from a non-checked-out distribution is risky." + fi + + if $use_gnulib; then + if test -z "$GNULIB_SRCDIR"; then + gnulib_path=$(test -f .gitmodules && + git config --file .gitmodules submodule.gnulib.path) + test -z "$gnulib_path" && gnulib_path=gnulib + GNULIB_SRCDIR=$gnulib_path + fi + fi + + # Die if there is no AC_CONFIG_AUX_DIR($build_aux) line in configure.ac. + found_aux_dir=no + grep '^[ ]*AC_CONFIG_AUX_DIR(\['"$build_aux"'])' configure.ac \ + >/dev/null && found_aux_dir=yes + grep '^[ ]*AC_CONFIG_AUX_DIR('"$build_aux"')' configure.ac \ + >/dev/null && found_aux_dir=yes + test $found_aux_dir = yes \ + || die "configure.ac lacks 'AC_CONFIG_AUX_DIR([$build_aux])'; add it" + + # If $build_aux doesn't exist, create it now, otherwise some bits + # below will malfunction. If creating it, also mark it as ignored. + if test ! -d $build_aux; then + mkdir $build_aux + for dot_ig in x $vc_ignore; do + test $dot_ig = x && continue + insert_vc_ignore $dot_ig $build_aux/ + done + fi + + check_build_prerequisites false + + use_libtool=0 + # We'd like to use grep -E, to see if any of LT_INIT, + # AC_PROG_LIBTOOL, AM_PROG_LIBTOOL is used in configure.ac, + # but that's not portable enough (e.g., for Solaris). + grep '^[ ]*A[CM]_PROG_LIBTOOL' configure.ac >/dev/null \ + && use_libtool=1 + grep '^[ ]*LT_INIT' configure.ac >/dev/null \ + && use_libtool=1 + if test $use_libtool = 1; then + find_tool LIBTOOLIZE glibtoolize libtoolize + fi + + if $use_gnulib; then + gnulib_tool=$GNULIB_SRCDIR/gnulib-tool + <$gnulib_tool || return + fi + + # NOTE: we have to be careful to run both autopoint and libtoolize + # before gnulib-tool, since gnulib-tool is likely to provide newer + # versions of files "installed" by these two programs. + # Then, *after* gnulib-tool (see below), we have to be careful to + # run autoreconf in such a way that it does not run either of these + # two just-pre-run programs. + + # Import from gettext. + with_gettext=yes + grep '^[ ]*AM_GNU_GETTEXT_VERSION(' configure.ac >/dev/null || \ + with_gettext=no + + if test $with_gettext = yes || test $use_libtool = 1; then + + tempbase=.bootstrap$$ + trap "rm -f $tempbase.0 $tempbase.1" HUP INT PIPE TERM + + > $tempbase.0 > $tempbase.1 && + find . ! -type d -print | sort > $tempbase.0 || return + + if test $with_gettext = yes; then + # Released autopoint has the tendency to install macros that have been + # obsoleted in current gnulib, so run this before gnulib-tool. + echo "$0: $AUTOPOINT --force" + $AUTOPOINT --force || return + fi + + # Autoreconf runs aclocal before libtoolize, which causes spurious + # warnings if the initial aclocal is confused by the libtoolized + # (or worse out-of-date) macro directory. + # libtoolize 1.9b added the --install option; but we support back + # to libtoolize 1.5.22, where the install action was default. + if test $use_libtool = 1; then + install= + case $($LIBTOOLIZE --help) in + *--install*) install=--install ;; + esac + echo "running: $LIBTOOLIZE $install --copy" + $LIBTOOLIZE $install --copy + fi + + find . ! -type d -print | sort >$tempbase.1 + old_IFS=$IFS + IFS=$nl + for file in $(comm -13 $tempbase.0 $tempbase.1); do + IFS=$old_IFS + parent=${file%/*} + version_controlled_file "$parent" "$file" || { + for dot_ig in x $vc_ignore; do + test $dot_ig = x && continue + ig=$parent/$dot_ig + insert_vc_ignore "$ig" "${file##*/}" + done + } + done + IFS=$old_IFS + + rm -f $tempbase.0 $tempbase.1 + trap - HUP INT PIPE TERM + fi + + # Import from gnulib. + + if $use_gnulib; then + gnulib_tool_options="\ + --no-changelog\ + --aux-dir=$build_aux\ + --doc-base=$doc_base\ + --lib=$gnulib_name\ + --m4-base=$m4_base/\ + --source-base=$source_base/\ + --tests-base=$tests_base\ + --local-dir=$local_gl_dir\ + $gnulib_tool_option_extras\ + " + if test $use_libtool = 1; then + case "$gnulib_tool_options " in + *' --libtool '*) ;; + *) gnulib_tool_options="$gnulib_tool_options --libtool" ;; + esac + fi + echo "$0: $gnulib_tool $gnulib_tool_options --import ..." + $gnulib_tool $gnulib_tool_options --import $gnulib_modules \ + || die "gnulib-tool failed" + + if test $with_gettext = yes && test ! -f $m4_base/gettext.m4; then + # The gnulib-tool invocation has removed $m4_base/gettext.m4, that the + # AUTOPOINT invocation had installed. This can occur when the gnulib + # module 'gettext' was previously present but is now not present any more. + # Repeat the AUTOPOINT invocation and the gnulib-tool invocation. + + echo "$0: $AUTOPOINT --force" + $AUTOPOINT --force || return + + echo "$0: $gnulib_tool $gnulib_tool_options --import ..." + $gnulib_tool $gnulib_tool_options --import $gnulib_modules \ + || die "gnulib-tool failed" + fi + + for file in $gnulib_files; do + symlink_to_dir "$GNULIB_SRCDIR" $file \ + || die "failed to symlink $file" + done + fi + + bootstrap_post_import_hook \ + || die "bootstrap_post_import_hook failed" + + # Remove any dangling symlink matching "*.m4" or "*.[ch]" in some + # gnulib-populated directories. Such .m4 files would cause aclocal to fail. + # The following requires GNU find 4.2.3 or newer. Considering the usual + # portability constraints of this script, that may seem a very demanding + # requirement, but it should be ok. Ignore any failure, which is fine, + # since this is only a convenience to help developers avoid the relatively + # unusual case in which a symlinked-to .m4 file is git-removed from gnulib + # between successive runs of this script. + find "$m4_base" "$source_base" \ + -depth \( -name '*.m4' -o -name '*.[ch]' \) \ + -type l -xtype l -delete > /dev/null 2>&1 + + # Invoke autoreconf with --force --install to ensure upgrades of tools + # such as ylwrap. + AUTORECONFFLAGS="--verbose --install --force $ACLOCAL_FLAGS" + AUTORECONFFLAGS="$AUTORECONFFLAGS --no-recursive" + + # Tell autoreconf not to invoke autopoint or libtoolize; they were run above. + echo "running: AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS" + AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS \ + || die "autoreconf failed" + + # Get some extra files from gnulib, overriding existing files. + for file in $gnulib_extra_files; do + case $file in + */INSTALL) dst=INSTALL;; + build-aux/*) dst=$build_aux/${file#build-aux/};; + *) dst=$file;; + esac + symlink_to_dir "$GNULIB_SRCDIR" $file $dst \ + || die "failed to symlink $file" + done + + if test $with_gettext = yes; then + # Create gettext configuration. + echo "$0: Creating po/Makevars from po/Makevars.template ..." + rm -f po/Makevars + sed ' + /^EXTRA_LOCALE_CATEGORIES *=/s/=.*/= '"$EXTRA_LOCALE_CATEGORIES"'/ + /^COPYRIGHT_HOLDER *=/s/=.*/= '"$COPYRIGHT_HOLDER"'/ + /^MSGID_BUGS_ADDRESS *=/s|=.*|= '"$MSGID_BUGS_ADDRESS"'| + /^XGETTEXT_OPTIONS *=/{ + s/$/ \\/ + a\ + '"$XGETTEXT_OPTIONS"' $${end_of_xgettext_options+} + } + ' po/Makevars.template >po/Makevars \ + || die 'cannot generate po/Makevars' + + # If the 'gettext' module is in use, grab the latest Makefile.in.in. + # If only the 'gettext-h' module is in use, assume autopoint already + # put the correct version of this file into place. + case $gnulib_modules in + *gettext-h*) ;; + *gettext*) + cp $GNULIB_SRCDIR/build-aux/po/Makefile.in.in po/Makefile.in.in \ + || die "cannot create po/Makefile.in.in" + ;; + esac + + if test -d runtime-po; then + # Similarly for runtime-po/Makevars, but not quite the same. + rm -f runtime-po/Makevars + sed ' + /^DOMAIN *=.*/s/=.*/= '"$package"'-runtime/ + /^subdir *=.*/s/=.*/= runtime-po/ + /^MSGID_BUGS_ADDRESS *=/s/=.*/= bug-'"$package"'@gnu.org/ + /^XGETTEXT_OPTIONS *=/{ + s/$/ \\/ + a\ + '"$XGETTEXT_OPTIONS_RUNTIME"' $${end_of_xgettext_options+} + } + ' po/Makevars.template >runtime-po/Makevars \ + || die 'cannot generate runtime-po/Makevars' + + # Copy identical files from po to runtime-po. + cp -p po/Makefile.in.in po/*-quot po/*.header po/*.sed po/*.sin runtime-po + fi + fi + + bootstrap_epilogue + + echo "$0: done. Now you can run './configure'." +} + +# ---------------------------------------------------------------------------- + +# Local Variables: +# eval: (add-hook 'before-save-hook 'time-stamp nil t) +# time-stamp-start: "scriptlibversion=" +# time-stamp-format: "%Y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: + +usage() { + cat </dev/null && found_aux_dir=yes -grep '^[ ]*AC_CONFIG_AUX_DIR('"$build_aux"')' configure.ac \ - >/dev/null && found_aux_dir=yes -test $found_aux_dir = yes \ - || die "configure.ac lacks 'AC_CONFIG_AUX_DIR([$build_aux])'; add it" +check_build_prerequisites $use_git -# If $build_aux doesn't exist, create it now, otherwise some bits -# below will malfunction. If creating it, also mark it as ignored. -if test ! -d $build_aux; then - mkdir $build_aux - for dot_ig in x $vc_ignore; do - test $dot_ig = x && continue - insert_vc_ignore $dot_ig $build_aux - done -fi - -# Note this deviates from the version comparison in automake -# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a -# but this should suffice as we won't be specifying old -# version formats or redundant trailing .0 in bootstrap.conf. -# If we did want full compatibility then we should probably -# use m4_version_compare from autoconf. -sort_ver() { # sort -V is not generally available - ver1="$1" - ver2="$2" - - # split on '.' and compare each component - i=1 - while : ; do - p1=$(echo "$ver1" | cut -d. -f$i) - p2=$(echo "$ver2" | cut -d. -f$i) - if [ ! "$p1" ]; then - echo "$1 $2" - break - elif [ ! "$p2" ]; then - echo "$2 $1" - break - elif [ ! "$p1" = "$p2" ]; then - if [ "$p1" -gt "$p2" ] 2>/dev/null; then # numeric comparison - echo "$2 $1" - elif [ "$p2" -gt "$p1" ] 2>/dev/null; then # numeric comparison - echo "$1 $2" - else # numeric, then lexicographic comparison - lp=$(printf "$p1\n$p2\n" | LANG=C sort -n | tail -n1) - if [ "$lp" = "$p2" ]; then - echo "$1 $2" - else - echo "$2 $1" - fi - fi - break - fi - i=$(($i+1)) - done -} - -get_version_sed=' -# Move version to start of line. -s/.*[v ]\([0-9]\)/\1/ - -# Skip lines that do not start with version. -/^[0-9]/!d - -# Remove characters after the version. -s/[^.a-z0-9-].*// - -# The first component must be digits only. -s/^\([0-9]*\)[a-z-].*/\1/ - -#the following essentially does s/5.005/5.5/ -s/\.0*\([1-9]\)/.\1/g -p -q' - -get_version() { - app=$1 - - $app --version >/dev/null 2>&1 || { $app --version; return 1; } - - $app --version 2>&1 | sed -n "$get_version_sed" -} - -check_versions() { - ret=0 - - while read app req_ver; do - # We only need libtoolize from the libtool package. - if test "$app" = libtool; then - app=libtoolize - fi - # Exempt git if --no-git is in effect. - if test "$app" = git; then - $use_git || continue - fi - # Honor $APP variables ($TAR, $AUTOCONF, etc.) - appvar=$(echo $app | LC_ALL=C tr '[a-z]-' '[A-Z]_') - test "$appvar" = TAR && appvar=AMTAR - case $appvar in - GZIP) ;; # Do not use $GZIP: it contains gzip options. - PERL::*) ;; # Keep perl modules as-is - *) eval "app=\${$appvar-$app}" ;; - esac - - # Handle the still-experimental Automake-NG programs specially. - # They remain named as the mainstream Automake programs ("automake", - # and "aclocal") to avoid gratuitous incompatibilities with - # pre-existing usages (by, say, autoreconf, or custom autogen.sh - # scripts), but correctly identify themselves (as being part of - # "GNU automake-ng") when asked their version. - case $app in - automake-ng|aclocal-ng) - app=${app%-ng} - ($app --version | grep '(GNU automake-ng)') >/dev/null 2>&1 || { - warn_ "Error: '$app' not found or not from Automake-NG" - ret=1 - continue - } ;; - # Another check is for perl modules. These can be written as - # e.g. perl::XML::XPath in case of XML::XPath module, etc. - perl::*) - # Extract module name - app="${app#perl::}" - if ! $PERL -m"$app" -e 'exit 0' >/dev/null 2>&1; then - warn_ "Error: perl module '$app' not found" - ret=1 - fi - continue - ;; - esac - if [ "$req_ver" = "-" ]; then - # Merely require app to exist; not all prereq apps are well-behaved - # so we have to rely on $? rather than get_version. - if ! check_exists --verbose $app; then - warn_ "Error: '$app' not found" - ret=1 - fi - else - # Require app to produce a new enough version string. - inst_ver=$(get_version $app) - if [ ! "$inst_ver" ]; then - warn_ "Error: '$app' not found" - ret=1 - else - latest_ver=$(sort_ver $req_ver $inst_ver | cut -d' ' -f2) - if [ ! "$latest_ver" = "$inst_ver" ]; then - warnf_ '%s\n' \ - "Error: '$app' version == $inst_ver is too old" \ - " '$app' version >= $req_ver is required" - ret=1 - fi - fi - fi - done - - return $ret -} - -print_versions() { - echo "Program Min_version" - echo "----------------------" - printf %s "$buildreq" - echo "----------------------" - # can't depend on column -t -} - -# Find sha1sum, named gsha1sum on MacPorts, shasum on Mac OS X 10.6. -# Also find the compatible sha1 utility on the BSDs -if test x"$SKIP_PO" = x; then - find_tool SHA1SUM sha1sum gsha1sum shasum sha1 -fi - -use_libtool=0 -# We'd like to use grep -E, to see if any of LT_INIT, -# AC_PROG_LIBTOOL, AM_PROG_LIBTOOL is used in configure.ac, -# but that's not portable enough (e.g., for Solaris). -grep '^[ ]*A[CM]_PROG_LIBTOOL' configure.ac >/dev/null \ - && use_libtool=1 -grep '^[ ]*LT_INIT' configure.ac >/dev/null \ - && use_libtool=1 -if test $use_libtool = 1; then - find_tool LIBTOOLIZE glibtoolize libtoolize -fi - -# gnulib-tool requires at least automake and autoconf. -# If either is not listed, add it (with minimum version) as a prerequisite. -case $buildreq in - *automake*) ;; - *) buildreq="automake 1.9 -$buildreq" ;; -esac -case $buildreq in - *autoconf*) ;; - *) buildreq="autoconf 2.59 -$buildreq" ;; -esac - -# When we can deduce that gnulib-tool will require patch, -# and when patch is not already listed as a prerequisite, add it, too. -if test -d "$local_gl_dir" \ - && ! find "$local_gl_dir" -name '*.diff' -exec false {} +; then - case $buildreq in - *patch*) ;; - *) buildreq="patch - -$buildreq" ;; - esac -fi - -if ! printf "$buildreq" | check_versions; then - echo >&2 - if test -f README-prereq; then - die "See README-prereq for how to get the prerequisite programs" - else - die "Please install the prerequisite programs" - fi -fi - -# Warn the user if autom4te appears to be broken; this causes known -# issues with at least gettext 0.18.3. -probe=$(echo 'm4_quote([hi])' | autom4te -l M4sugar -t 'm4_quote:$%' -) -if test "x$probe" != xhi; then - warn_ "WARNING: your autom4te wrapper eats stdin;" - warn_ "if bootstrap fails, consider upgrading your autotools" +if $bootstrap_sync; then + prepare_GNULIB_SRCDIR + upgrade_bootstrap "$@" + # Since we have now upgraded if needed, no need to try it a second time below. + bootstrap_sync=false fi echo "$0: Bootstrapping from checked-out $package sources..." -# See if we can use gnulib's git-merge-changelog merge driver. -if $use_git && test -d .git && check_exists git; then - if git config merge.merge-changelog.driver >/dev/null ; then - : - elif check_exists git-merge-changelog; then - echo "$0: initializing git-merge-changelog driver" - git config merge.merge-changelog.name 'GNU-style ChangeLog merge driver' - git config merge.merge-changelog.driver 'git-merge-changelog %O %A %B' - else - echo "$0: consider installing git-merge-changelog from gnulib" - fi +# Pass GNULIB_SRCDIR and GNULIB_REFDIR to any subsidiary commands that care. +export GNULIB_SRCDIR +export GNULIB_REFDIR + +if $pull && { $use_git || test -z "$SKIP_PO"; }; then + autopull \ + `if $bootstrap_sync; then + echo ' --bootstrap-sync' + else + echo ' --no-bootstrap-sync' + fi` \ + `if test -z "$checkout_only_file"; then echo ' --force'; fi` \ + `if ! $use_git; then echo ' --no-git'; fi` \ + `if test -n "$SKIP_PO"; then echo ' --skip-po'; fi` \ + || die "could not fetch auxiliary files" fi - -cleanup_gnulib() { - status=$? - rm -fr "$gnulib_path" - exit $status -} - -git_modules_config () { - test -f .gitmodules && git config --file .gitmodules "$@" -} - -if $use_gnulib; then - if $use_git; then - gnulib_path=$(git_modules_config submodule.gnulib.path) - test -z "$gnulib_path" && gnulib_path=gnulib - fi - - # Get gnulib files. Populate $GNULIB_SRCDIR, possibly updating a - # submodule, for use in the rest of the script. - - case ${GNULIB_SRCDIR--} in - -) - # Note that $use_git is necessarily true in this case. - if git_modules_config submodule.gnulib.url >/dev/null; then - echo "$0: getting gnulib files..." - git submodule init -- "$gnulib_path" || exit $? - git submodule update -- "$gnulib_path" || exit $? - - elif [ ! -d "$gnulib_path" ]; then - echo "$0: getting gnulib files..." - - trap cleanup_gnulib 1 2 13 15 - - shallow= - if test -z "$GNULIB_REVISION"; then - git clone -h 2>&1 | grep -- --depth > /dev/null && shallow='--depth 2' - git clone $shallow ${GNULIB_URL:-$default_gnulib_url} "$gnulib_path" \ - || cleanup_gnulib - else - git fetch -h 2>&1 | grep -- --depth > /dev/null && shallow='--depth 2' - mkdir -p "$gnulib_path" - # Only want a shallow checkout of $GNULIB_REVISION, but git does not - # support cloning by commit hash. So attempt a shallow fetch by commit - # hash to minimize the amount of data downloaded and changes needed to - # be processed, which can drastically reduce download and processing - # time for checkout. If the fetch by commit fails, a shallow fetch can - # not be performed because we do not know what the depth of the commit - # is without fetching all commits. So fallback to fetching all commits. - git -C "$gnulib_path" init - git -C "$gnulib_path" remote add origin ${GNULIB_URL:-$default_gnulib_url} - git -C "$gnulib_path" fetch $shallow origin "$GNULIB_REVISION" \ - || git -C "$gnulib_path" fetch origin \ - || cleanup_gnulib - git -C "$gnulib_path" reset --hard FETCH_HEAD - fi - - trap - 1 2 13 15 - fi - GNULIB_SRCDIR=$gnulib_path - ;; - *) - # Use GNULIB_SRCDIR directly or as a reference. - if $use_git && test -d "$GNULIB_SRCDIR"/.git && \ - git_modules_config submodule.gnulib.url >/dev/null; then - echo "$0: getting gnulib files..." - if git submodule -h|grep -- --reference > /dev/null; then - # Prefer the one-liner available in git 1.6.4 or newer. - git submodule update --init --reference "$GNULIB_SRCDIR" \ - "$gnulib_path" || exit $? - else - # This fallback allows at least git 1.5.5. - if test -f "$gnulib_path"/gnulib-tool; then - # Since file already exists, assume submodule init already complete. - git submodule update -- "$gnulib_path" || exit $? - else - # Older git can't clone into an empty directory. - rmdir "$gnulib_path" 2>/dev/null - git clone --reference "$GNULIB_SRCDIR" \ - "$(git_modules_config submodule.gnulib.url)" "$gnulib_path" \ - && git submodule init -- "$gnulib_path" \ - && git submodule update -- "$gnulib_path" \ - || exit $? - fi - fi - GNULIB_SRCDIR=$gnulib_path - fi - ;; - esac - - if test -d "$GNULIB_SRCDIR"/.git && test -n "$GNULIB_REVISION" \ - && ! git_modules_config submodule.gnulib.url >/dev/null; then - (cd "$GNULIB_SRCDIR" && git checkout "$GNULIB_REVISION") || cleanup_gnulib - fi - - # $GNULIB_SRCDIR now points to the version of gnulib to use, and - # we no longer need to use git or $gnulib_path below here. - - if $bootstrap_sync; then - cmp -s "$0" "$GNULIB_SRCDIR/build-aux/bootstrap" || { - echo "$0: updating bootstrap and restarting..." - case $(sh -c 'echo "$1"' -- a) in - a) ignored=--;; - *) ignored=ignored;; - esac - exec sh -c \ - 'cp "$1" "$2" && shift && exec "${CONFIG_SHELL-/bin/sh}" "$@"' \ - $ignored "$GNULIB_SRCDIR/build-aux/bootstrap" \ - "$0" "$@" --no-bootstrap-sync - } - fi - - gnulib_tool=$GNULIB_SRCDIR/gnulib-tool - <$gnulib_tool || exit $? +if $gen; then + autogen \ + `if $copy; then echo ' --copy'; fi` \ + `if test -z "$checkout_only_file"; then echo ' --force'; fi` \ + || die "could not generate auxiliary files" fi -# Get translations. - -download_po_files() { - subdir=$1 - domain=$2 - echo "$me: getting translations into $subdir for $domain..." - cmd=$(printf "$po_download_command_format" "$subdir" "$domain") - eval "$cmd" -} - -# Mirror .po files to $po_dir/.reference and copy only the new -# or modified ones into $po_dir. Also update $po_dir/LINGUAS. -# Note po files that exist locally only are left in $po_dir but will -# not be included in LINGUAS and hence will not be distributed. -update_po_files() { - # Directory containing primary .po files. - # Overwrite them only when we're sure a .po file is new. - po_dir=$1 - domain=$2 - - # Mirror *.po files into this dir. - # Usually contains *.s1 checksum files. - ref_po_dir="$po_dir/.reference" - - test -d $ref_po_dir || mkdir $ref_po_dir || return - download_po_files $ref_po_dir $domain \ - && ls "$ref_po_dir"/*.po 2>/dev/null | - sed 's|.*/||; s|\.po$||' > "$po_dir/LINGUAS" || return - - langs=$(cd $ref_po_dir && echo *.po | sed 's/\.po//g') - test "$langs" = '*' && langs=x - for po in $langs; do - case $po in x) continue;; esac - new_po="$ref_po_dir/$po.po" - cksum_file="$ref_po_dir/$po.s1" - if ! test -f "$cksum_file" || - ! test -f "$po_dir/$po.po" || - ! $SHA1SUM -c "$cksum_file" < "$new_po" > /dev/null 2>&1; then - echo "$me: updated $po_dir/$po.po..." - cp "$new_po" "$po_dir/$po.po" \ - && $SHA1SUM < "$new_po" > "$cksum_file" || return - fi - done -} - -case $SKIP_PO in -'') - if test -d po; then - update_po_files po $package || exit - fi - - if test -d runtime-po; then - update_po_files runtime-po $package-runtime || exit - fi;; -esac - -version_controlled_file() { - parent=$1 - file=$2 - if test -d .git; then - git rm -n "$file" > /dev/null 2>&1 - elif test -d .svn; then - svn log -r HEAD "$file" > /dev/null 2>&1 - elif test -d CVS; then - grep -F "/${file##*/}/" "$parent/CVS/Entries" 2>/dev/null | - grep '^/[^/]*/[0-9]' > /dev/null - else - warn_ "no version control for $file?" - false - fi -} - -# NOTE: we have to be careful to run both autopoint and libtoolize -# before gnulib-tool, since gnulib-tool is likely to provide newer -# versions of files "installed" by these two programs. -# Then, *after* gnulib-tool (see below), we have to be careful to -# run autoreconf in such a way that it does not run either of these -# two just-pre-run programs. - -# Import from gettext. -with_gettext=yes -grep '^[ ]*AM_GNU_GETTEXT_VERSION(' configure.ac >/dev/null || \ - with_gettext=no - -if test $with_gettext = yes || test $use_libtool = 1; then - - tempbase=.bootstrap$$ - trap "rm -f $tempbase.0 $tempbase.1" 1 2 13 15 - - > $tempbase.0 > $tempbase.1 && - find . ! -type d -print | sort > $tempbase.0 || exit - - if test $with_gettext = yes; then - # Released autopoint has the tendency to install macros that have been - # obsoleted in current gnulib, so run this before gnulib-tool. - echo "$0: $AUTOPOINT --force" - $AUTOPOINT --force || exit - fi - - # Autoreconf runs aclocal before libtoolize, which causes spurious - # warnings if the initial aclocal is confused by the libtoolized - # (or worse out-of-date) macro directory. - # libtoolize 1.9b added the --install option; but we support back - # to libtoolize 1.5.22, where the install action was default. - if test $use_libtool = 1; then - install= - case $($LIBTOOLIZE --help) in - *--install*) install=--install ;; - esac - echo "running: $LIBTOOLIZE $install --copy" - $LIBTOOLIZE $install --copy - fi - - find . ! -type d -print | sort >$tempbase.1 - old_IFS=$IFS - IFS=$nl - for file in $(comm -13 $tempbase.0 $tempbase.1); do - IFS=$old_IFS - parent=${file%/*} - version_controlled_file "$parent" "$file" || { - for dot_ig in x $vc_ignore; do - test $dot_ig = x && continue - ig=$parent/$dot_ig - insert_vc_ignore "$ig" "${file##*/}" - done - } - done - IFS=$old_IFS - - rm -f $tempbase.0 $tempbase.1 - trap - 1 2 13 15 -fi - -# Import from gnulib. - -if $use_gnulib; then - gnulib_tool_options="\ - --no-changelog\ - --aux-dir=$build_aux\ - --doc-base=$doc_base\ - --lib=$gnulib_name\ - --m4-base=$m4_base/\ - --source-base=$source_base/\ - --tests-base=$tests_base\ - --local-dir=$local_gl_dir\ - $gnulib_tool_option_extras\ - " - if test $use_libtool = 1; then - case "$gnulib_tool_options " in - *' --libtool '*) ;; - *) gnulib_tool_options="$gnulib_tool_options --libtool" ;; - esac - fi - echo "$0: $gnulib_tool $gnulib_tool_options --import ..." - $gnulib_tool $gnulib_tool_options --import $gnulib_modules \ - || die "gnulib-tool failed" - - for file in $gnulib_files; do - symlink_to_dir "$GNULIB_SRCDIR" $file \ - || die "failed to symlink $file" - done -fi - -bootstrap_post_import_hook \ - || die "bootstrap_post_import_hook failed" - -# Don't proceed if there are uninitialized submodules. In particular, -# the next step will remove dangling links, which might be links into -# uninitialized submodules. -# -# Uninitialized submodules are listed with an initial dash. -if $use_git && git submodule | grep '^-' >/dev/null; then - die "some git submodules are not initialized. " \ - "Run 'git submodule update --init' and bootstrap again." -fi - -# Remove any dangling symlink matching "*.m4" or "*.[ch]" in some -# gnulib-populated directories. Such .m4 files would cause aclocal to fail. -# The following requires GNU find 4.2.3 or newer. Considering the usual -# portability constraints of this script, that may seem a very demanding -# requirement, but it should be ok. Ignore any failure, which is fine, -# since this is only a convenience to help developers avoid the relatively -# unusual case in which a symlinked-to .m4 file is git-removed from gnulib -# between successive runs of this script. -find "$m4_base" "$source_base" \ - -depth \( -name '*.m4' -o -name '*.[ch]' \) \ - -type l -xtype l -delete > /dev/null 2>&1 - -# Invoke autoreconf with --force --install to ensure upgrades of tools -# such as ylwrap. -AUTORECONFFLAGS="--verbose --install --force -I $m4_base $ACLOCAL_FLAGS" - -# Some systems (RHEL 5) are using ancient autotools, for which the -# --no-recursive option had not been invented. Detect that lack and -# omit the option when it's not supported. FIXME in 2017: remove this -# hack when RHEL 5 autotools are updated, or when they become irrelevant. -case $($AUTORECONF --help) in - *--no-recursive*) AUTORECONFFLAGS="$AUTORECONFFLAGS --no-recursive";; -esac - -# Tell autoreconf not to invoke autopoint or libtoolize; they were run above. -echo "running: AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS" -AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS \ - || die "autoreconf failed" - -# Get some extra files from gnulib, overriding existing files. -for file in $gnulib_extra_files; do - case $file in - */INSTALL) dst=INSTALL;; - build-aux/*) dst=$build_aux/${file#build-aux/};; - *) dst=$file;; - esac - symlink_to_dir "$GNULIB_SRCDIR" $file $dst \ - || die "failed to symlink $file" -done - -if test $with_gettext = yes; then - # Create gettext configuration. - echo "$0: Creating po/Makevars from po/Makevars.template ..." - rm -f po/Makevars - sed ' - /^EXTRA_LOCALE_CATEGORIES *=/s/=.*/= '"$EXTRA_LOCALE_CATEGORIES"'/ - /^COPYRIGHT_HOLDER *=/s/=.*/= '"$COPYRIGHT_HOLDER"'/ - /^MSGID_BUGS_ADDRESS *=/s|=.*|= '"$MSGID_BUGS_ADDRESS"'| - /^XGETTEXT_OPTIONS *=/{ - s/$/ \\/ - a\ - '"$XGETTEXT_OPTIONS"' $${end_of_xgettext_options+} - } - ' po/Makevars.template >po/Makevars \ - || die 'cannot generate po/Makevars' - - # If the 'gettext' module is in use, grab the latest Makefile.in.in. - # If only the 'gettext-h' module is in use, assume autopoint already - # put the correct version of this file into place. - case $gnulib_modules in - *gettext-h*) ;; - *gettext*) - cp $GNULIB_SRCDIR/build-aux/po/Makefile.in.in po/Makefile.in.in \ - || die "cannot create po/Makefile.in.in" - ;; - esac - - if test -d runtime-po; then - # Similarly for runtime-po/Makevars, but not quite the same. - rm -f runtime-po/Makevars - sed ' - /^DOMAIN *=.*/s/=.*/= '"$package"'-runtime/ - /^subdir *=.*/s/=.*/= runtime-po/ - /^MSGID_BUGS_ADDRESS *=/s/=.*/= bug-'"$package"'@gnu.org/ - /^XGETTEXT_OPTIONS *=/{ - s/$/ \\/ - a\ - '"$XGETTEXT_OPTIONS_RUNTIME"' $${end_of_xgettext_options+} - } - ' po/Makevars.template >runtime-po/Makevars \ - || die 'cannot generate runtime-po/Makevars' - - # Copy identical files from po to runtime-po. - (cd po && cp -p Makefile.in.in *-quot *.header *.sed *.sin ../runtime-po) - fi -fi - -bootstrap_epilogue - -echo "$0: done. Now you can run './configure'." +# ---------------------------------------------------------------------------- # Local Variables: -# eval: (add-hook 'before-save-hook 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp nil t) # time-stamp-start: "scriptversion=" -# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-format: "%Y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: From 6837293b874cfbcf00eb294e6fc3b5811b759127 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Wed, 27 Aug 2025 17:32:13 -0500 Subject: [PATCH 027/208] lib/datetime: Support dates outside of 1901..2038 range Fixes: https://savannah.gnu.org/bugs/?63894 Fixes: https://savannah.gnu.org/bugs/?66301 Signed-off-by: Vladimir Serbinenko Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- grub-core/lib/datetime.c | 65 ++++++++++++++++++++++++++++++++-------- include/grub/datetime.h | 34 ++++++++++++++------- 2 files changed, 76 insertions(+), 23 deletions(-) diff --git a/grub-core/lib/datetime.c b/grub-core/lib/datetime.c index 8f0922fb0..dbce17e03 100644 --- a/grub-core/lib/datetime.c +++ b/grub-core/lib/datetime.c @@ -64,7 +64,10 @@ grub_get_weekday_name (struct grub_datetime *datetime) #define SECPERDAY (24*SECPERHOUR) #define DAYSPERYEAR 365 #define DAYSPER4YEARS (4*DAYSPERYEAR+1) - +/* 24 leap years in 100 years */ +#define DAYSPER100YEARS (100 * DAYSPERYEAR + 24) +/* 97 leap years in 400 years */ +#define DAYSPER400YEARS (400 * DAYSPERYEAR + 97) void grub_unixtime2datetime (grub_int64_t nix, struct grub_datetime *datetime) @@ -76,10 +79,12 @@ grub_unixtime2datetime (grub_int64_t nix, struct grub_datetime *datetime) /* Convenience: let's have 3 consecutive non-bissextile years at the beginning of the counting date. So count from 1901. */ int days_epoch; - /* Number of days since 1st Januar, 1901. */ + /* Number of days since 1st January, 1 (proleptic). */ unsigned days; /* Seconds into current day. */ unsigned secs_in_day; + /* Tracks whether this is a leap year. */ + bool bisextile; /* Transform C divisions and modulos to mathematical ones */ if (nix < 0) @@ -92,27 +97,63 @@ grub_unixtime2datetime (grub_int64_t nix, struct grub_datetime *datetime) days_epoch = grub_divmod64 (nix, SECPERDAY, NULL); secs_in_day = nix - days_epoch * SECPERDAY; - days = days_epoch + 69 * DAYSPERYEAR + 17; + /* + * 1970 is Unix Epoch. Adjust to a year 1 epoch: + * Leap year logic: + * - Years evenly divisible by 400 are leap years + * - Otherwise, if divisible by 100 are not leap years + * - Otherwise, if divisible by 4 are leap years + * There are four 400-year periods (1600 years worth of days with leap days) + * There are 369 years in addition to the four 400 year periods + * There are three 100-year periods worth of leap days (3*24) + * There are 17 leap days in 69 years (beyond the three 100 year periods) + */ + days = 4 * DAYSPER400YEARS + 369 * DAYSPERYEAR + 3 * 24 + 17 + days_epoch; - datetime->year = 1901 + 4 * (days / DAYSPER4YEARS); + datetime->year = 1 + 400 * (days / DAYSPER400YEARS); + days %= DAYSPER400YEARS; + + /* + * On 31st December of bissextile (leap) years 365 days from the beginning + * of the year elapsed but year isn't finished yet - every 400 years + * 396 is 4 years less than 400 year leap cycle + * 96 is 1 day less than number of leap days in 400 years + */ + if (days / DAYSPER100YEARS == 4) + { + datetime->year += 396; + days -= 396 * DAYSPERYEAR + 96; + } + else + { + datetime->year += 100 * (days / DAYSPER100YEARS); + days %= DAYSPER100YEARS; + } + + datetime->year += 4 * (days / DAYSPER4YEARS); days %= DAYSPER4YEARS; - /* On 31st December of bissextile years 365 days from the beginning - of the year elapsed but year isn't finished yet */ + /* + * On 31st December of bissextile (leap) years 365 days from the beginning + * of the year elapsed but year isn't finished yet - every 4 years + */ if (days / DAYSPERYEAR == 4) { datetime->year += 3; - days -= 3*DAYSPERYEAR; + days -= 3 * DAYSPERYEAR; } else { datetime->year += days / DAYSPERYEAR; days %= DAYSPERYEAR; } - for (i = 0; i < 12 - && days >= (i==1 && datetime->year % 4 == 0 - ? 29 : months[i]); i++) - days -= (i==1 && datetime->year % 4 == 0 - ? 29 : months[i]); + + bisextile = (datetime->year % 4 == 0 + && (datetime->year % 100 != 0 + || datetime->year % 400 == 0)) ? true : false; + for (i = 0; + i < 12 && days >= ((i == 1 && bisextile == true) ? 29 : months[i]); + i++) + days -= ((i == 1 && bisextile == true) ? 29 : months[i]); datetime->month = i + 1; datetime->day = 1 + days; datetime->hour = (secs_in_day / SECPERHOUR); diff --git a/include/grub/datetime.h b/include/grub/datetime.h index bcec636f0..17303be9c 100644 --- a/include/grub/datetime.h +++ b/include/grub/datetime.h @@ -54,8 +54,9 @@ void grub_unixtime2datetime (grub_int64_t nix, static inline int grub_datetime2unixtime (const struct grub_datetime *datetime, grub_int64_t *nix) { - grub_int32_t ret; + grub_int64_t ret; int y4, ay; + bool bisextile; const grub_uint16_t monthssum[12] = { 0, 31, @@ -75,15 +76,11 @@ grub_datetime2unixtime (const struct grub_datetime *datetime, grub_int64_t *nix) const int SECPERHOUR = 60 * SECPERMIN; const int SECPERDAY = 24 * SECPERHOUR; const int SECPERYEAR = 365 * SECPERDAY; - const int SECPER4YEARS = 4 * SECPERYEAR + SECPERDAY; + const grub_int64_t SECPER4YEARS = 4 * SECPERYEAR + SECPERDAY; - if (datetime->year > 2038 || datetime->year < 1901) - return 0; if (datetime->month > 12 || datetime->month < 1) return 0; - /* In the period of validity of unixtime all years divisible by 4 - are bissextile*/ /* Convenience: let's have 3 consecutive non-bissextile years at the beginning of the epoch. So count from 1973 instead of 1970 */ ret = 3 * SECPERYEAR + SECPERDAY; @@ -91,16 +88,31 @@ grub_datetime2unixtime (const struct grub_datetime *datetime, grub_int64_t *nix) /* Transform C divisions and modulos to mathematical ones */ y4 = ((datetime->year - 1) >> 2) - (1973 / 4); ay = datetime->year - 1973 - 4 * y4; - ret += y4 * SECPER4YEARS; - ret += ay * SECPERYEAR; + ret += (grub_int64_t) y4 * SECPER4YEARS; + ret += (grub_int64_t) ay * SECPERYEAR; - ret += monthssum[datetime->month - 1] * SECPERDAY; - if (ay == 3 && datetime->month >= 3) + /* + * Correct above calculation (which assumes every 4 years is a leap year) + * to remove those "false leap years" that are divisible by 100 but not 400. + * Since this logic starts with seconds since 1973, 15 is used because: + * - (1973 - 1) / 100 = 19 (floor due to integer math) + * - (1973 - 1) / 400 = 4 (floor due to integer math) + * - 19 - 4 - 15 = 0 (we want to start with no "false leap years" at time + * zero of 1973) + */ + ret -= ((datetime->year - 1) / 100 - (datetime->year - 1) / 400 - 15) + * SECPERDAY; + + ret += (grub_int64_t) monthssum[datetime->month - 1] * SECPERDAY; + bisextile = (ay == 3 + && (datetime->year % 100 != 0 + || datetime->year % 400 == 0)) ? true : false; + if (bisextile == true && datetime->month >= 3) ret += SECPERDAY; ret += (datetime->day - 1) * SECPERDAY; if ((datetime->day > months[datetime->month - 1] - && (!ay || datetime->month != 2 || datetime->day != 29)) + && !(bisextile == true && datetime->month == 2 && datetime->day == 29)) || datetime->day < 1) return 0; From dfa3dbf61ee8dc2442dec9e67db76219d85b9465 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Wed, 27 Aug 2025 17:32:14 -0500 Subject: [PATCH 028/208] tests: Test dates outside of 32-bit Unix range Add tests outside the date range possible with 32-bit time calculation. Signed-off-by: Vladimir Serbinenko Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- tests/date_unit_test.c | 75 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 9 deletions(-) diff --git a/tests/date_unit_test.c b/tests/date_unit_test.c index 99774f199..08edc54df 100644 --- a/tests/date_unit_test.c +++ b/tests/date_unit_test.c @@ -25,12 +25,13 @@ #include static void -date_test (grub_int32_t v) +date_test (grub_int64_t v) { struct grub_datetime dt; time_t t = v; struct tm *g; int w; + grub_int64_t back = 0; g = gmtime (&t); @@ -38,28 +39,56 @@ date_test (grub_int32_t v) w = grub_get_weekday (&dt); - grub_test_assert (g->tm_sec == dt.second, "time %d bad second: %d vs %d", v, + grub_datetime2unixtime (&dt, &back); + + grub_test_assert (g->tm_sec == dt.second, "time %lld bad second: %d vs %d", (long long) v, g->tm_sec, dt.second); - grub_test_assert (g->tm_min == dt.minute, "time %d bad minute: %d vs %d", v, + grub_test_assert (g->tm_min == dt.minute, "time %lld bad minute: %d vs %d", (long long) v, g->tm_min, dt.minute); - grub_test_assert (g->tm_hour == dt.hour, "time %d bad hour: %d vs %d", v, + grub_test_assert (g->tm_hour == dt.hour, "time %lld bad hour: %d vs %d", (long long) v, g->tm_hour, dt.hour); - grub_test_assert (g->tm_mday == dt.day, "time %d bad day: %d vs %d", v, + grub_test_assert (g->tm_mday == dt.day, "time %lld bad day: %d vs %d", (long long) v, g->tm_mday, dt.day); - grub_test_assert (g->tm_mon + 1 == dt.month, "time %d bad month: %d vs %d", v, + grub_test_assert (g->tm_mon + 1 == dt.month, "time %lld bad month: %d vs %d",(long long) v, g->tm_mon + 1, dt.month); grub_test_assert (g->tm_year + 1900 == dt.year, - "time %d bad year: %d vs %d", v, + "time %lld bad year: %d vs %d", (long long) v, g->tm_year + 1900, dt.year); - grub_test_assert (g->tm_wday == w, "time %d bad week day: %d vs %d", v, + grub_test_assert (g->tm_wday == w, "time %lld bad week day: %d vs %d", (long long) v, g->tm_wday, w); + grub_test_assert (back == v, "time %lld bad back transform: %lld", (long long) v, + (long long) back); } static void date_test_iter (void) { - grub_int32_t tests[] = { -1, 0, +1, -2133156255, GRUB_INT32_MIN, + /* + * Test several interesting UNIX timestamps in 32-bit time: + * 1. -1: 1969-12-31 23:59:59 - Just before EPOCH + * 2. 0: 1970-01-01 00:00:00 - EPOCH + * 3. +1: 1970-01-01 00:00:01 - Just after EPOCH + * 4. 978224552: 2000-12-31 01:02:32 - Leap year, after Feb + * 5. -2133156255: 1902-05-28 16:35:45 - Nominal value + * 6. -2110094321: 1903-02-19 14:41:19 - Nominal value + * 7. GRUB_INT32_MIN: 1901-12-13 20:45:52 - 32-bit Min value + * 8. GRUB_INT32_MAX: 2038-01-19 03:14:07 - 32-bit Max value + */ + grub_int32_t tests[] = { -1, 0, +1, 978224552, -2133156255, -2110094321, GRUB_INT32_MIN, GRUB_INT32_MAX }; + /* + * Test several known UNIX timestamps outside 32-bit time: + * 1. 5774965200: 2152-12-31 21:00:00 - Leap year + * 2. 4108700725: 2100-03-14 09:45:25 - Not a leap year + * 3. -5029179792: 1810-08-19 21:36:48 - Not a leap year + * 4. -62135596799: 0001-01-01 00:00:00 - Minimum AD + * 5. 253402300799: 9999-12-31 23:59:59 - Maximum 4 digit year + */ + grub_int64_t tests64[] = { (grub_int64_t) 5774965200, + (grub_int64_t) 4108700725, + (grub_int64_t) -5029179792, + (grub_int64_t) -62135596799, + (grub_int64_t) 253402300799 }; unsigned i; for (i = 0; i < ARRAY_SIZE (tests); i++) @@ -71,6 +100,34 @@ date_test_iter (void) date_test (x); date_test (-x); } + + if (sizeof (time_t) > 4) + { + for (i = 0; i < ARRAY_SIZE (tests64); i++) + date_test (tests64[i]); + for (i = 0; i < 5000000; i++) + { + /* + * Test some pseudo-random dates/times between 1970 and 9999 + * "117" is used to scale the random 32-bit int from range + * 0..2147483648 to 0..251255586816. This is reasonably + * close to max 9999 date represented by 253402300799. + */ + grub_int64_t x = (grub_int64_t) rand () * (grub_int64_t) 117; + date_test (x); + } + for (i = 0; i < 5000000; i++) + { + /* + * Test some pseudo-random dates/times between 0001 and 1969 + * "-28" is used to scale the random 32-bit int from range + * 0..2147483648 to -60129542144..0. This is reasonably + * close to min 0001 date represented by -62135596799. + */ + grub_int64_t x = (grub_int64_t) rand () * (grub_int64_t) -28; + date_test (x); + } + } } GRUB_UNIT_TEST ("date_unit_test", date_test_iter); From 1d2ee8f8b351bc4c545f68cdec79ce0dcb7c9e49 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sun, 31 Aug 2025 21:40:49 -0500 Subject: [PATCH 029/208] tests: Add test ISO files to dist package Add test ISO files to dist package to allow ISO test to pass. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- conf/Makefile.extra-dist | 3 +++ 1 file changed, 3 insertions(+) diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index 49bca5577..10cbd4fa4 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -158,6 +158,9 @@ EXTRA_DIST += po/hebrew.sed EXTRA_DIST += tests/dfly-mbr-mbexample.mbr.img.gz EXTRA_DIST += tests/dfly-mbr-mbexample.dfly.img.gz +EXTRA_DIST += tests/iso9660_ce_loop2.iso.gz +EXTRA_DIST += tests/iso9660_ce_loop.iso.gz +EXTRA_DIST += tests/iso9660_early_ce.iso.gz EXTRA_DIST += coreboot.cfg From 1f9092bfd57bd7cb8d5bd3f6d7431af37c081c85 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sat, 30 Aug 2025 22:32:39 -0500 Subject: [PATCH 030/208] libgcrypt: Allow GRUB to build with Clang Attempts to build GRUB with Clang were failing due to errors such as: error: redefinition of typedef 'gcry_md_hd_t' is a C11 feature Correct this by adding a compiler pragma to disable the Clang "typedef-redefinition" warnings. This required an update to include/grub/crypto.h and the util/import_gcry.py script to add the pragma to libgcrypt-grub's types.h due to u16 and similar types. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- include/grub/crypto.h | 8 ++++++++ util/import_gcry.py | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/include/grub/crypto.h b/include/grub/crypto.h index b0d7add1d..6ab021cb8 100644 --- a/include/grub/crypto.h +++ b/include/grub/crypto.h @@ -245,6 +245,14 @@ typedef struct gcry_md_spec struct gcry_md_spec *next; } gcry_md_spec_t; +/* + * Clang defaults to flagging type redefinitions as warnings. + * A few "forward declarations" in this file are needed due + * to inter-relationship complexities between GRUB and libgcrypt. + */ +#ifdef __clang__ +#pragma GCC diagnostic ignored "-Wtypedef-redefinition" +#endif typedef struct gcry_md_handle*gcry_md_hd_t; struct gcry_mpi; diff --git a/util/import_gcry.py b/util/import_gcry.py index 086bde77c..1e23ee760 100644 --- a/util/import_gcry.py +++ b/util/import_gcry.py @@ -608,7 +608,11 @@ with codecs.open (os.path.join (cipher_dir_out, "crypto.lst"), "w", "utf-8") as continue with codecs.open (infile, "r", "utf-8") as f: if src == "types.h": - fw.write (f.read ().replace ("float f;", "").replace ("double g;", "")) + fw.write (f.read ().replace ("float f;", "").replace ("double g;", "") \ + .replace("#ifndef HAVE_BYTE", + "#ifdef __clang__\n" \ + "#pragma GCC diagnostic ignored \"-Wtypedef-redefinition\"\n#endif\n" \ + "#ifndef HAVE_BYTE")) continue if src == "cipher-proto.h": From abb8fb6d1af1fe3db06b97bfae87466685b23007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=ABlle=20Cazuc?= Date: Tue, 2 Sep 2025 11:50:51 +0200 Subject: [PATCH 031/208] util/grub-mkimagexx: Fix riscv32 relocation offset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using grub-mkrescue for a riscv32 target, an invalid implicit cast on the offset calculation produces an error during the relocation process: grub-mkrescue: error: target XXX not reachable from pc=fc. This patch adds an explicit grub_int64_t cast to compute the offset as a 64-bit subtraction. Signed-off-by: Anaëlle Cazuc Reviewed-by: Daniel Kiper --- util/grub-mkimagexx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c index 448862b2e..45ac77558 100644 --- a/util/grub-mkimagexx.c +++ b/util/grub-mkimagexx.c @@ -1354,7 +1354,7 @@ SUFFIX (relocate_addrs) (Elf_Ehdr *e, struct section_metadata *smd, */ sym_addr += addend; - off = sym_addr - target_section_addr - offset - image_target->vaddr_offset; + off = (grub_int64_t) sym_addr - target_section_addr - offset - image_target->vaddr_offset; switch (ELF_R_TYPE (info)) { From ee789e1a625a43b31e17f91a11d4cfb609bc4c52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=ABlle=20Cazuc?= Date: Tue, 2 Sep 2025 12:26:44 +0200 Subject: [PATCH 032/208] lib/b64dec: Use grub_size_t instead of size_t for _gpgrt_b64dec_proc() function definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some targets, size_t and grub_size_t may not be the same type (unsigned long / unsigned int). This breaks the compilation because the definition of _gpgrt_b64dec_proc() differs from gpgrt_b64dec_proc() declaration. Fix it by using grub_size_t in the _gpgrt_b64dec_proc() definition. Signed-off-by: Anaëlle Cazuc Reviewed-by: Daniel Kiper --- grub-core/lib/b64dec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/lib/b64dec.c b/grub-core/lib/b64dec.c index 868d98568..769cabda4 100644 --- a/grub-core/lib/b64dec.c +++ b/grub-core/lib/b64dec.c @@ -100,8 +100,8 @@ _gpgrt_b64dec_start (const char *title) /* Do in-place decoding of base-64 data of LENGTH in BUFFER. Stores the new length of the buffer at R_NBYTES. */ gpg_err_code_t -_gpgrt_b64dec_proc (gpgrt_b64state_t state, void *buffer, size_t length, - size_t *r_nbytes) +_gpgrt_b64dec_proc (gpgrt_b64state_t state, void *buffer, grub_size_t length, + grub_size_t *r_nbytes) { enum decoder_states ds = state->idx; unsigned char val = state->radbuf[0]; From 31cc7dfe50f680f979eec8ffe7d4d6cc8a46e965 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:54:46 +0530 Subject: [PATCH 033/208] powerpc/ieee1275: Add support for signing GRUB with an appended signature Add infrastructure to allow firmware to verify the integrity of GRUB by use of a Linux-kernel-module-style appended signature. We initially target powerpc-ieee1275, but the code should be extensible to other platforms. Usually these signatures are appended to a file without modifying the ELF file itself. (This is what the 'sign-file' tool does, for example.) The verifier loads the signed file from the file system and looks at the end of the file for the appended signature. However, on powerpc-ieee1275 platforms, the bootloader is often stored directly in the PReP partition as raw bytes without a file-system. This makes determining the location of an appended signature more difficult. To address this, we add a new ELF Note. The name field of shall be the string "Appended-Signature", zero-padded to 4 byte alignment. The type field shall be 0x41536967 (the ASCII values for the string "ASig"). It must be the final section in the ELF binary. The description shall contain the appended signature structure as defined by the Linux kernel. The description will also be padded to be a multiple of 4 bytes. The padding shall be added before the appended signature structure (not at the end) so that the final bytes of a signed ELF file are the appended signature magic. A subsequent patch documents how to create a GRUB core.img validly signed under this scheme. Signed-off-by: Rashmica Gupta Signed-off-by: Daniel Axtens Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Stefan Berger Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- include/grub/util/install.h | 7 +++++-- include/grub/util/mkimage.h | 4 ++-- util/grub-install-common.c | 17 +++++++++++++--- util/grub-mkimage.c | 12 +++++++++++ util/grub-mkimagexx.c | 40 ++++++++++++++++++++++++++++++++++++- util/mkimage.c | 9 ++++++--- 6 files changed, 78 insertions(+), 11 deletions(-) diff --git a/include/grub/util/install.h b/include/grub/util/install.h index 5c0a52ca2..3aabc4285 100644 --- a/include/grub/util/install.h +++ b/include/grub/util/install.h @@ -69,6 +69,8 @@ N_("disable shim_lock verifier"), 0 }, \ { "disable-cli", GRUB_INSTALL_OPTIONS_DISABLE_CLI, 0, 0, \ N_("disabled command line interface access"), 0 }, \ + { "appended-signature-size", GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE, \ + "SIZE", 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), 1}, \ { "verbose", 'v', 0, 0, \ N_("print verbose messages."), 1 } @@ -132,7 +134,8 @@ enum grub_install_options { GRUB_INSTALL_OPTIONS_DTB, GRUB_INSTALL_OPTIONS_SBAT, GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, - GRUB_INSTALL_OPTIONS_DISABLE_CLI + GRUB_INSTALL_OPTIONS_DISABLE_CLI, + GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE }; extern char *grub_install_source_directory; @@ -192,7 +195,7 @@ grub_install_generate_image (const char *dir, const char *prefix, size_t npubkeys, char *config_path, const struct grub_install_image_target_desc *image_target, - int note, + int note, size_t appsig_size, grub_compression_t comp, const char *dtb_file, const char *sbat_path, const int disable_shim_lock, const int disable_cli); diff --git a/include/grub/util/mkimage.h b/include/grub/util/mkimage.h index 9d74f82c5..0d40383eb 100644 --- a/include/grub/util/mkimage.h +++ b/include/grub/util/mkimage.h @@ -51,12 +51,12 @@ grub_mkimage_load_image64 (const char *kernel_path, const struct grub_install_image_target_desc *image_target); void grub_mkimage_generate_elf32 (const struct grub_install_image_target_desc *image_target, - int note, char *sbat, char **core_img, size_t *core_size, + int note, char *sbat, size_t appsig_size, char **core_img, size_t *core_size, Elf32_Addr target_addr, struct grub_mkimage_layout *layout); void grub_mkimage_generate_elf64 (const struct grub_install_image_target_desc *image_target, - int note, char *sbat, char **core_img, size_t *core_size, + int note, char *sbat, size_t appsig_size, char **core_img, size_t *core_size, Elf64_Addr target_addr, struct grub_mkimage_layout *layout); diff --git a/util/grub-install-common.c b/util/grub-install-common.c index 22bccb6a3..102ab18b0 100644 --- a/util/grub-install-common.c +++ b/util/grub-install-common.c @@ -467,10 +467,13 @@ static char *sbat; static int disable_shim_lock; static grub_compression_t compression; static int disable_cli; +static size_t appsig_size; int grub_install_parse (int key, char *arg) { + const char *end; + switch (key) { case GRUB_INSTALL_OPTIONS_INSTALL_CORE_COMPRESS: @@ -571,6 +574,13 @@ grub_install_parse (int key, char *arg) grub_util_error (_("Unrecognized compression `%s'"), arg); case GRUB_INSTALL_OPTIONS_GRUB_MKIMAGE: return 1; + case GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE: + appsig_size = grub_strtoul (arg, &end, 10); + if (*arg == '\0' || *end != '\0') + grub_util_error (_("non-numeric or invalid appended signature size `%s'"), arg); + else if (appsig_size == 0) + grub_util_error (_("appended signature size `%s', and it should not be zero"), arg); + return 1; default: return 0; } @@ -683,9 +693,10 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, *p = '\0'; grub_util_info ("grub-mkimage --directory '%s' --prefix '%s' --output '%s'" - " --format '%s' --compression '%s'%s%s%s%s\n", + " --format '%s' --compression '%s'" + " --appended-signature-size %zu %s %s %s %s\n", dir, prefix, outname, - mkimage_target, compnames[compression], + mkimage_target, compnames[compression], appsig_size, note ? " --note" : "", disable_shim_lock ? " --disable-shim-lock" : "", disable_cli ? " --disable-cli" : "", s); @@ -698,7 +709,7 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, grub_install_generate_image (dir, prefix, fp, outname, modules.entries, memdisk_path, pubkeys, npubkeys, config_path, tgt, - note, compression, dtb, sbat, + note, appsig_size, compression, dtb, sbat, disable_shim_lock, disable_cli); while (dc--) grub_install_pop_module (); diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c index 547f7310f..d3a5aaa5f 100644 --- a/util/grub-mkimage.c +++ b/util/grub-mkimage.c @@ -84,6 +84,7 @@ static struct argp_option options[] = { {"sbat", 's', N_("FILE"), 0, N_("SBAT metadata"), 0}, {"disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, N_("disable shim_lock verifier"), 0}, {"disable-cli", GRUB_INSTALL_OPTIONS_DISABLE_CLI, 0, 0, N_("disable command line interface access"), 0}, + {"appended-signature-size", 'S', N_("SIZE"), 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), 0}, {"verbose", 'v', 0, 0, N_("print verbose messages."), 0}, { 0, 0, 0, 0, 0, 0 } }; @@ -130,6 +131,7 @@ struct arguments int note; int disable_shim_lock; int disable_cli; + size_t appsig_size; const struct grub_install_image_target_desc *image_target; grub_compression_t comp; }; @@ -140,6 +142,7 @@ argp_parser (int key, char *arg, struct argp_state *state) /* Get the input argument from argp_parse, which we know is a pointer to our arguments structure. */ struct arguments *arguments = state->input; + const char *end; switch (key) { @@ -172,6 +175,14 @@ argp_parser (int key, char *arg, struct argp_state *state) arguments->note = 1; break; + case 'S': + arguments->appsig_size = grub_strtoul (arg, &end, 10); + if (*arg == '\0' || *end != '\0') + grub_util_error (_("non-numeric or invalid appended signature size `%s'"), arg); + else if (arguments->appsig_size == 0) + grub_util_error (_("appended signature size `%s', and it should not be zero"), arg); + break; + case 'm': if (arguments->memdisk) free (arguments->memdisk); @@ -330,6 +341,7 @@ main (int argc, char *argv[]) arguments.memdisk, arguments.pubkeys, arguments.npubkeys, arguments.config, arguments.image_target, arguments.note, + arguments.appsig_size, arguments.comp, arguments.dtb, arguments.sbat, arguments.disable_shim_lock, arguments.disable_cli); diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c index 45ac77558..7fe2e35e6 100644 --- a/util/grub-mkimagexx.c +++ b/util/grub-mkimagexx.c @@ -115,6 +115,14 @@ struct grub_sbat_note { char name[ALIGN_UP(sizeof(GRUB_SBAT_NOTE_NAME), 4)]; }; +#define GRUB_APPENDED_SIGNATURE_NOTE_NAME "Appended-Signature" +#define GRUB_APPENDED_SIGNATURE_NOTE_TYPE 0x41536967 /* "ASig" */ +struct grub_appended_signature_note +{ + Elf32_Nhdr header; + char name[ALIGN_UP (sizeof (GRUB_APPENDED_SIGNATURE_NOTE_NAME), 4)]; +}; + static int is_relocatable (const struct grub_install_image_target_desc *image_target) { @@ -216,7 +224,7 @@ grub_arm_reloc_jump24 (grub_uint32_t *target, Elf32_Addr sym_addr) void SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc *image_target, - int note, char *sbat, char **core_img, size_t *core_size, + int note, char *sbat, size_t appsig_size, char **core_img, size_t *core_size, Elf_Addr target_addr, struct grub_mkimage_layout *layout) { @@ -237,6 +245,12 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc footer_size += ALIGN_UP (sizeof (struct grub_sbat_note) + layout->sbat_size, 4); } + if (appsig_size) + { + phnum++; + footer_size += ALIGN_UP (sizeof (struct grub_appended_signature_note), 4); + } + if (image_target->id != IMAGE_LOONGSON_ELF) phnum += 2; @@ -518,6 +532,30 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc memcpy (note_ptr->name, GRUB_SBAT_NOTE_NAME, sizeof (GRUB_SBAT_NOTE_NAME)); memcpy ((char *)(note_ptr + 1), sbat, layout->sbat_size); + phdr++; + phdr->p_type = grub_host_to_target32 (PT_NOTE); + phdr->p_flags = grub_host_to_target32 (PF_R); + phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof); + phdr->p_vaddr = 0; + phdr->p_paddr = 0; + phdr->p_filesz = grub_host_to_target32 (note_size); + phdr->p_memsz = 0; + phdr->p_offset = grub_host_to_target32 (header_size + program_size + footer_offset); + footer += note_size; + footer_offset += note_size; + } + + if (appsig_size) + { + int note_size = ALIGN_UP (sizeof (struct grub_appended_signature_note) + appsig_size, 4); + struct grub_appended_signature_note *note_ptr = (struct grub_appended_signature_note *) footer; + + note_ptr->header.n_namesz = grub_host_to_target32 (sizeof (GRUB_APPENDED_SIGNATURE_NOTE_NAME)); + /* Needs to sit at the end, so we round this up and sign some zero padding. */ + note_ptr->header.n_descsz = grub_host_to_target32 (ALIGN_UP (appsig_size, 4)); + note_ptr->header.n_type = grub_host_to_target32 (GRUB_APPENDED_SIGNATURE_NOTE_TYPE); + strcpy (note_ptr->name, GRUB_APPENDED_SIGNATURE_NOTE_NAME); + phdr++; phdr->p_type = grub_host_to_target32 (PT_NOTE); phdr->p_flags = grub_host_to_target32 (PF_R); diff --git a/util/mkimage.c b/util/mkimage.c index b46df2909..9618b37cf 100644 --- a/util/mkimage.c +++ b/util/mkimage.c @@ -885,7 +885,7 @@ grub_install_generate_image (const char *dir, const char *prefix, char *memdisk_path, char **pubkey_paths, size_t npubkeys, char *config_path, const struct grub_install_image_target_desc *image_target, - int note, grub_compression_t comp, const char *dtb_path, + int note, size_t appsig_size, grub_compression_t comp, const char *dtb_path, const char *sbat_path, int disable_shim_lock, int disable_cli) { @@ -946,6 +946,9 @@ grub_install_generate_image (const char *dir, const char *prefix, if (sbat_path != NULL && (image_target->id != IMAGE_EFI && image_target->id != IMAGE_PPC)) grub_util_error (_("SBAT data can be added only to EFI or powerpc-ieee1275 images")); + if (appsig_size != 0 && image_target->id != IMAGE_PPC) + grub_util_error (_("appended signature can be support only to powerpc-ieee1275 images")); + if (disable_shim_lock) total_module_size += sizeof (struct grub_module_header); @@ -1833,10 +1836,10 @@ grub_install_generate_image (const char *dir, const char *prefix, else target_addr = image_target->link_addr; if (image_target->voidp_sizeof == 4) - grub_mkimage_generate_elf32 (image_target, note, sbat, &core_img, &core_size, + grub_mkimage_generate_elf32 (image_target, note, sbat, appsig_size, &core_img, &core_size, target_addr, &layout); else - grub_mkimage_generate_elf64 (image_target, note, sbat, &core_img, &core_size, + grub_mkimage_generate_elf64 (image_target, note, sbat, appsig_size, &core_img, &core_size, target_addr, &layout); } break; From f826cc8b0e2b3f2cb66ea26104c6d66425191d24 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Mon, 6 Oct 2025 12:54:47 +0530 Subject: [PATCH 034/208] crypto: Move storage for grub_crypto_pk_* to crypto.c The way gcry_rsa and friends (the asymmetric ciphers) are loaded for the pgp module is a bit quirky. include/grub/crypto.h contains: extern struct gcry_pk_spec *grub_crypto_pk_rsa; commands/pgp.c contains the actual storage: struct gcry_pk_spec *grub_crypto_pk_rsa; And the module itself saves to the storage in pgp.c: GRUB_MOD_INIT(gcry_rsa) { grub_crypto_pk_rsa = &_gcry_pubkey_spec_rsa; } This is annoying: gcry_rsa now has a dependency on pgp! We want to be able to bring in gcry_rsa without bringing in PGP, so move the storage to crypto.c. Previously, gcry_rsa depended on pgp and mpi. Now it depends on crypto and mpi. As pgp depends on crypto, this doesn't add any new module dependencies using the PGP verfier. [FWIW, the story is different for the symmetric ciphers. cryptodisk and friends (zfs encryption etc) use grub_crypto_lookup_cipher_by_name() to get a cipher handle. That depends on grub_ciphers being populated by people calling grub_cipher_register. import_gcry.py ensures that the symmetric ciphers call it.] Signed-off-by: Daniel Axtens Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Stefan Berger Reviewed-by: Vladimir Serbinenko Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/commands/pgp.c | 4 ---- grub-core/lib/crypto.c | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c index e61887862..251ed1b06 100644 --- a/grub-core/commands/pgp.c +++ b/grub-core/commands/pgp.c @@ -136,10 +136,6 @@ struct signature_v4_header grub_uint16_t hashed_sub; } GRUB_PACKED; -struct gcry_pk_spec *grub_crypto_pk_dsa; -struct gcry_pk_spec *grub_crypto_pk_ecdsa; -struct gcry_pk_spec *grub_crypto_pk_rsa; - struct { const char *name; diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c index dd60dd4ac..292b747b2 100644 --- a/grub-core/lib/crypto.c +++ b/grub-core/lib/crypto.c @@ -170,6 +170,10 @@ grub_md_unregister (gcry_md_spec_t *cipher) } } +struct gcry_pk_spec *grub_crypto_pk_dsa; +struct gcry_pk_spec *grub_crypto_pk_ecdsa; +struct gcry_pk_spec *grub_crypto_pk_rsa; + void grub_crypto_hash (const gcry_md_spec_t *hash, void *out, const void *in, grub_size_t inlen) From aefe0de22eaf7260c298f5dc95fd7c03fffdec9a Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:54:48 +0530 Subject: [PATCH 035/208] pgp: Rename OBJ_TYPE_PUBKEY to OBJ_TYPE_GPG_PUBKEY Prior to the addition of the X.509 public key support for appended signature, current PGP signature relied on the GPG public key. Changing the enum name from "OBJ_TYPE_PUBKEY" to "OBJ_TYPE_GPG_PUBKEY" to differentiate between x509 certificate based appended signature and GPG certificate based PGP signature. Signed-off-by: Alastair D'Silva Signed-off-by: Daniel Axtens Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Stefan Berger Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- docs/grub.texi | 34 +++++++++++++++++----------------- grub-core/commands/pgp.c | 2 +- include/grub/kernel.h | 2 +- util/grub-mkimage.c | 2 +- util/mkimage.c | 2 +- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index b81eb1d93..37297fc2c 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -3357,8 +3357,8 @@ entry when selected. @node check_signatures @subsection check_signatures -This variable controls whether GRUB enforces digital signature -validation on loaded files. @xref{Using digital signatures}. +This variable controls whether GRUB enforces GPG-style digital signature +validation on loaded files. @xref{Using GPG-style digital signatures}. @node chosen @subsection chosen @@ -7054,7 +7054,7 @@ These keys are used to validate signatures when environment variable @code{check_signatures} is set to @code{enforce} (@pxref{check_signatures}), and by some invocations of @command{verify_detached} (@pxref{verify_detached}). @xref{Using -digital signatures}, for more information. +GPG-style digital signatures}, for more information. @end deffn @node drivemap @@ -7470,7 +7470,7 @@ The output is in GPG's v4 key fingerprint format (i.e., the output of @code{gpg --fingerprint}). The least significant four bytes (last eight hexadecimal digits) can be used as an argument to @command{distrust} (@pxref{distrust}). -@xref{Using digital signatures}, for more information about uses for +@xref{Using GPG-style digital signatures}, for more information about uses for these keys. @end deffn @@ -7505,7 +7505,7 @@ When used with care, @option{--skip-sig} and the whitelist enable an administrator to configure a system to boot only signed configurations, but to allow the user to select from among multiple configurations, and to enable ``one-shot'' boot attempts and -``savedefault'' behavior. @xref{Using digital signatures}, for more +``savedefault'' behavior. @xref{Using GPG-style digital signatures}, for more information. @end deffn @@ -7877,7 +7877,7 @@ read. It is possible to modify a digitally signed environment block file from within GRUB using this command, such that its signature will no longer be valid on subsequent boots. Care should be taken in such advanced configurations to avoid rendering the system -unbootable. @xref{Using digital signatures}, for more information. +unbootable. @xref{Using GPG-style digital signatures}, for more information. @end deffn @@ -8367,7 +8367,7 @@ signatures when environment variable @code{check_signatures} is set to must itself be properly signed. The @option{--skip-sig} option can be used to disable signature-checking when reading @var{pubkey_file} itself. It is expected that @option{--skip-sig} is useful for testing -and manual booting. @xref{Using digital signatures}, for more +and manual booting. @xref{Using GPG-style digital signatures}, for more information. @end deffn @@ -8440,7 +8440,7 @@ tried. Exit code @code{$?} is set to 0 if the signature validates successfully. If validation fails, it is set to a non-zero value. -@xref{Using digital signatures}, for more information. +@xref{Using GPG-style digital signatures}, for more information. @end deffn @node videoinfo @@ -8900,13 +8900,13 @@ environment variables and commands are listed in the same order. @chapter Security @menu -* Authentication and authorisation:: Users and access control -* Using digital signatures:: Booting digitally signed code -* UEFI secure boot and shim:: Booting digitally signed PE files -* Secure Boot Advanced Targeting:: Embedded information for generation number based revocation -* Measured Boot:: Measuring boot components -* Lockdown:: Lockdown when booting on a secure setup -* TPM2 key protector:: Managing disk key with TPM2 key protector +* Authentication and authorisation:: Users and access control +* Using GPG-style digital signatures:: Booting digitally signed code +* UEFI secure boot and shim:: Booting digitally signed PE files +* Secure Boot Advanced Targeting:: Embedded information for generation number based revocation +* Measured Boot:: Measuring boot components +* Lockdown:: Lockdown when booting on a secure setup +* TPM2 key protector:: Managing disk key with TPM2 key protector @end menu @node Authentication and authorisation @@ -8982,8 +8982,8 @@ generating configuration files with authentication. You can use adding @kbd{set superusers=} and @kbd{password} or @kbd{password_pbkdf2} commands. -@node Using digital signatures -@section Using digital signatures in GRUB +@node Using GPG-style digital signatures +@section Using GPG-style digital signatures in GRUB GRUB's @file{core.img} can optionally provide enforcement that all files subsequently read from disk are covered by a valid digital signature. diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c index 251ed1b06..a2549f9fd 100644 --- a/grub-core/commands/pgp.c +++ b/grub-core/commands/pgp.c @@ -920,7 +920,7 @@ GRUB_MOD_INIT(pgp) grub_memset (&pseudo_file, 0, sizeof (pseudo_file)); /* Not an ELF module, skip. */ - if (header->type != OBJ_TYPE_PUBKEY) + if (header->type != OBJ_TYPE_GPG_PUBKEY) continue; pseudo_file.fs = &pseudo_fs; diff --git a/include/grub/kernel.h b/include/grub/kernel.h index 6121c1e66..885289f5d 100644 --- a/include/grub/kernel.h +++ b/include/grub/kernel.h @@ -28,7 +28,7 @@ enum OBJ_TYPE_MEMDISK, OBJ_TYPE_CONFIG, OBJ_TYPE_PREFIX, - OBJ_TYPE_PUBKEY, + OBJ_TYPE_GPG_PUBKEY, OBJ_TYPE_DTB, OBJ_TYPE_DISABLE_SHIM_LOCK, OBJ_TYPE_DISABLE_CLI diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c index d3a5aaa5f..9370e609f 100644 --- a/util/grub-mkimage.c +++ b/util/grub-mkimage.c @@ -75,7 +75,7 @@ static struct argp_option options[] = { /* TRANSLATORS: "embed" is a verb (command description). "*/ {"config", 'c', N_("FILE"), 0, N_("embed FILE as an early config"), 0}, /* TRANSLATORS: "embed" is a verb (command description). "*/ - {"pubkey", 'k', N_("FILE"), 0, N_("embed FILE as public key for signature checking"), 0}, + {"pubkey", 'k', N_("FILE"), 0, N_("embed FILE as public key for PGP signature checking"), 0}, /* TRANSLATORS: NOTE is a name of segment. */ {"note", 'n', 0, 0, N_("add NOTE segment for CHRP IEEE1275"), 0}, {"output", 'o', N_("FILE"), 0, N_("output a generated image to FILE [default=stdout]"), 0}, diff --git a/util/mkimage.c b/util/mkimage.c index 9618b37cf..61c4e78e2 100644 --- a/util/mkimage.c +++ b/util/mkimage.c @@ -1056,7 +1056,7 @@ grub_install_generate_image (const char *dir, const char *prefix, curs = grub_util_get_image_size (pubkey_paths[i]); header = (struct grub_module_header *) (kernel_img + offset); - header->type = grub_host_to_target32 (OBJ_TYPE_PUBKEY); + header->type = grub_host_to_target32 (OBJ_TYPE_GPG_PUBKEY); header->size = grub_host_to_target32 (curs + sizeof (*header)); offset += sizeof (*header); From 1fca5f397abb6ae42a94439ed0d1f8ea4449a052 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:54:49 +0530 Subject: [PATCH 036/208] grub-install: Support embedding x509 certificates To support verification of appended signatures, we need a way to embed the necessary public keys. Existing appended signature schemes in the Linux kernel use X.509 certificates, so allow certificates to be embedded in the GRUB core image in the same way as PGP keys. Signed-off-by: Alastair D'Silva Signed-off-by: Daniel Axtens Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Stefan Berger Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- include/grub/kernel.h | 1 + include/grub/util/install.h | 3 +++ util/grub-install-common.c | 19 +++++++++++++++++- util/grub-mkimage.c | 12 +++++++++++- util/mkimage.c | 39 ++++++++++++++++++++++++++++++++++++- 5 files changed, 71 insertions(+), 3 deletions(-) diff --git a/include/grub/kernel.h b/include/grub/kernel.h index 885289f5d..9f3e2031f 100644 --- a/include/grub/kernel.h +++ b/include/grub/kernel.h @@ -29,6 +29,7 @@ enum OBJ_TYPE_CONFIG, OBJ_TYPE_PREFIX, OBJ_TYPE_GPG_PUBKEY, + OBJ_TYPE_X509_PUBKEY, OBJ_TYPE_DTB, OBJ_TYPE_DISABLE_SHIM_LOCK, OBJ_TYPE_DISABLE_CLI diff --git a/include/grub/util/install.h b/include/grub/util/install.h index 3aabc4285..6f27e2e42 100644 --- a/include/grub/util/install.h +++ b/include/grub/util/install.h @@ -69,6 +69,8 @@ N_("disable shim_lock verifier"), 0 }, \ { "disable-cli", GRUB_INSTALL_OPTIONS_DISABLE_CLI, 0, 0, \ N_("disabled command line interface access"), 0 }, \ + { "x509key", 'x', N_("FILE"), 0, \ + N_("embed FILE as an x509 certificate for appended signature checking"), 0}, \ { "appended-signature-size", GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE, \ "SIZE", 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), 1}, \ { "verbose", 'v', 0, 0, \ @@ -193,6 +195,7 @@ grub_install_generate_image (const char *dir, const char *prefix, const char *outname, char *mods[], char *memdisk_path, char **pubkey_paths, size_t npubkeys, + char **x509key_paths, size_t nx509keys, char *config_path, const struct grub_install_image_target_desc *image_target, int note, size_t appsig_size, diff --git a/util/grub-install-common.c b/util/grub-install-common.c index 102ab18b0..a913ee61c 100644 --- a/util/grub-install-common.c +++ b/util/grub-install-common.c @@ -463,6 +463,8 @@ handle_install_list (struct install_list *il, const char *val, static char **pubkeys; static size_t npubkeys; +static char **x509keys; +static size_t nx509keys; static char *sbat; static int disable_shim_lock; static grub_compression_t compression; @@ -511,6 +513,10 @@ grub_install_parse (int key, char *arg) case GRUB_INSTALL_OPTIONS_DISABLE_CLI: disable_cli = 1; return 1; + case 'x': + x509keys = xrealloc (x509keys, sizeof (x509keys[0]) * (nx509keys + 1)); + x509keys[nx509keys++] = xstrdup (arg); + return 1; case GRUB_INSTALL_OPTIONS_VERBOSITY: verbosity++; @@ -642,6 +648,9 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, for (pk = pubkeys; pk < pubkeys + npubkeys; pk++) slen += sizeof (" --pubkey ''") + grub_strlen (*pk); + for (pk = x509keys; pk < x509keys + nx509keys; pk++) + slen += sizeof (" --x509key ''") + grub_strlen (*pk); + for (md = modules.entries; *md; md++) slen += sizeof (" ''") + grub_strlen (*md); @@ -682,6 +691,14 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, *p++ = '\''; } + for (pk = x509keys; pk < x509keys + nx509keys; pk++) + { + p = grub_stpcpy (p, "--x509key '"); + p = grub_stpcpy (p, *pk); + *p++ = '\''; + *p++ = ' '; + } + for (md = modules.entries; *md; md++) { *p++ = ' '; @@ -708,7 +725,7 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, grub_install_generate_image (dir, prefix, fp, outname, modules.entries, memdisk_path, - pubkeys, npubkeys, config_path, tgt, + pubkeys, npubkeys, x509keys, nx509keys, config_path, tgt, note, appsig_size, compression, dtb, sbat, disable_shim_lock, disable_cli); while (dc--) diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c index 9370e609f..fb14aa731 100644 --- a/util/grub-mkimage.c +++ b/util/grub-mkimage.c @@ -76,6 +76,7 @@ static struct argp_option options[] = { {"config", 'c', N_("FILE"), 0, N_("embed FILE as an early config"), 0}, /* TRANSLATORS: "embed" is a verb (command description). "*/ {"pubkey", 'k', N_("FILE"), 0, N_("embed FILE as public key for PGP signature checking"), 0}, + {"x509key", 'x', N_("FILE"), 0, N_("embed FILE as an x509 certificate for appended signature checking"), 0}, /* TRANSLATORS: NOTE is a name of segment. */ {"note", 'n', 0, 0, N_("add NOTE segment for CHRP IEEE1275"), 0}, {"output", 'o', N_("FILE"), 0, N_("output a generated image to FILE [default=stdout]"), 0}, @@ -125,6 +126,8 @@ struct arguments char *dtb; char **pubkeys; size_t npubkeys; + char **x509keys; + size_t nx509keys; char *font; char *config; char *sbat; @@ -209,6 +212,12 @@ argp_parser (int key, char *arg, struct argp_state *state) arguments->pubkeys[arguments->npubkeys++] = xstrdup (arg); break; + case 'x': + arguments->x509keys = xrealloc (arguments->x509keys, + sizeof (arguments->x509keys[0]) * (arguments->nx509keys + 1)); + arguments->x509keys[arguments->nx509keys++] = xstrdup (arg); + break; + case 'c': if (arguments->config) free (arguments->config); @@ -339,7 +348,8 @@ main (int argc, char *argv[]) grub_install_generate_image (arguments.dir, arguments.prefix, fp, arguments.output, arguments.modules, arguments.memdisk, arguments.pubkeys, - arguments.npubkeys, arguments.config, + arguments.npubkeys, arguments.x509keys, + arguments.nx509keys, arguments.config, arguments.image_target, arguments.note, arguments.appsig_size, arguments.comp, arguments.dtb, diff --git a/util/mkimage.c b/util/mkimage.c index 61c4e78e2..f364a5718 100644 --- a/util/mkimage.c +++ b/util/mkimage.c @@ -883,7 +883,7 @@ void grub_install_generate_image (const char *dir, const char *prefix, FILE *out, const char *outname, char *mods[], char *memdisk_path, char **pubkey_paths, - size_t npubkeys, char *config_path, + size_t npubkeys, char **x509key_paths, size_t nx509keys, char *config_path, const struct grub_install_image_target_desc *image_target, int note, size_t appsig_size, grub_compression_t comp, const char *dtb_path, const char *sbat_path, int disable_shim_lock, @@ -929,6 +929,24 @@ grub_install_generate_image (const char *dir, const char *prefix, } } + if (nx509keys != 0 && image_target->id != IMAGE_PPC) + grub_util_error (_("x509 public key can be support only to appended signature" + " with powerpc-ieee1275 images")); + + { + size_t i; + + for (i = 0; i < nx509keys; i++) + { + size_t curs; + + curs = ALIGN_ADDR (grub_util_get_image_size (x509key_paths[i])); + grub_util_info ("the size of x509 public key %u is 0x%" GRUB_HOST_PRIxLONG_LONG, + (unsigned) i, (unsigned long long) curs); + total_module_size += curs + sizeof (struct grub_module_header); + } + } + if (memdisk_path) { memdisk_size = ALIGN_UP(grub_util_get_image_size (memdisk_path), 512); @@ -1065,6 +1083,25 @@ grub_install_generate_image (const char *dir, const char *prefix, } } + { + size_t i; + + for (i = 0; i < nx509keys; i++) + { + size_t curs; + struct grub_module_header *header; + + curs = grub_util_get_image_size (x509key_paths[i]); + header = (struct grub_module_header *) (kernel_img + offset); + header->type = grub_host_to_target32 (OBJ_TYPE_X509_PUBKEY); + header->size = grub_host_to_target32 (curs + sizeof (*header)); + + offset += sizeof (*header); + grub_util_load_image (x509key_paths[i], kernel_img + offset); + offset += ALIGN_ADDR (curs); + } + } + if (memdisk_path) { struct grub_module_header *header; From 7d28bdb0b212465bab9aea5f0147fdb3484396c6 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:54:50 +0530 Subject: [PATCH 037/208] appended signatures: Import GNUTLS's ASN.1 description files In order to parse PKCS#7 messages and X.509 certificates with libtasn1, we need some information about how they are encoded. We get these from GNUTLS, which has the benefit that they support the features we need and are well tested. The GNUTLS files are from: - https://github.com/gnutls/gnutls/blob/master/lib/gnutls.asn - https://github.com/gnutls/gnutls/blob/master/lib/pkix.asn The GNUTLS license is LGPLv2.1+, which is GPLv3 compatible, allowing us to import it without issue. Signed-off-by: Daniel Axtens Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Stefan Berger Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- .../commands/appendedsig/gnutls_asn1_tab.c | 148 ++++++ .../commands/appendedsig/pkix_asn1_tab.c | 485 ++++++++++++++++++ 2 files changed, 633 insertions(+) create mode 100644 grub-core/commands/appendedsig/gnutls_asn1_tab.c create mode 100644 grub-core/commands/appendedsig/pkix_asn1_tab.c diff --git a/grub-core/commands/appendedsig/gnutls_asn1_tab.c b/grub-core/commands/appendedsig/gnutls_asn1_tab.c new file mode 100644 index 000000000..efc0c145a --- /dev/null +++ b/grub-core/commands/appendedsig/gnutls_asn1_tab.c @@ -0,0 +1,148 @@ +#include +#include + +/* + * Imported from gnutls.asn. + * https://github.com/gnutls/gnutls/blob/master/lib/gnutls.asn + */ +const asn1_static_node grub_gnutls_asn1_tab[] = { + { "GNUTLS", 536872976, NULL }, + { NULL, 1073741836, NULL }, + { "RSAPublicKey", 1610612741, NULL }, + { "modulus", 1073741827, NULL }, + { "publicExponent", 3, NULL }, + { "RSAPrivateKey", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "modulus", 1073741827, NULL }, + { "publicExponent", 1073741827, NULL }, + { "privateExponent", 1073741827, NULL }, + { "prime1", 1073741827, NULL }, + { "prime2", 1073741827, NULL }, + { "exponent1", 1073741827, NULL }, + { "exponent2", 1073741827, NULL }, + { "coefficient", 1073741827, NULL }, + { "otherPrimeInfos", 16386, "OtherPrimeInfos"}, + { "ProvableSeed", 1610612741, NULL }, + { "algorithm", 1073741836, NULL }, + { "seed", 7, NULL }, + { "OtherPrimeInfos", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "OtherPrimeInfo"}, + { "OtherPrimeInfo", 1610612741, NULL }, + { "prime", 1073741827, NULL }, + { "exponent", 1073741827, NULL }, + { "coefficient", 3, NULL }, + { "AlgorithmIdentifier", 1610612741, NULL }, + { "algorithm", 1073741836, NULL }, + { "parameters", 541081613, NULL }, + { "algorithm", 1, NULL }, + { "DigestInfo", 1610612741, NULL }, + { "digestAlgorithm", 1073741826, "DigestAlgorithmIdentifier"}, + { "digest", 7, NULL }, + { "DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, + { "DSAPublicKey", 1073741827, NULL }, + { "DSAParameters", 1610612741, NULL }, + { "p", 1073741827, NULL }, + { "q", 1073741827, NULL }, + { "g", 3, NULL }, + { "DSASignatureValue", 1610612741, NULL }, + { "r", 1073741827, NULL }, + { "s", 3, NULL }, + { "DSAPrivateKey", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "p", 1073741827, NULL }, + { "q", 1073741827, NULL }, + { "g", 1073741827, NULL }, + { "Y", 1073741827, NULL }, + { "priv", 3, NULL }, + { "DHParameter", 1610612741, NULL }, + { "prime", 1073741827, NULL }, + { "base", 1073741827, NULL }, + { "privateValueLength", 16387, NULL }, + { "pkcs-11-ec-Parameters", 1610612754, NULL }, + { "oId", 1073741836, NULL }, + { "curveName", 31, NULL }, + { "ECParameters", 1610612754, NULL }, + { "namedCurve", 12, NULL }, + { "ECPrivateKey", 1610612741, NULL }, + { "Version", 1073741827, NULL }, + { "privateKey", 1073741831, NULL }, + { "parameters", 1610637314, "ECParameters"}, + { NULL, 2056, "0"}, + { "publicKey", 536895494, NULL }, + { NULL, 2056, "1"}, + { "PrincipalName", 1610612741, NULL }, + { "name-type", 1610620931, NULL }, + { NULL, 2056, "0"}, + { "name-string", 536879115, NULL }, + { NULL, 1073743880, "1"}, + { NULL, 27, NULL }, + { "KRB5PrincipalName", 1610612741, NULL }, + { "realm", 1610620955, NULL }, + { NULL, 2056, "0"}, + { "principalName", 536879106, "PrincipalName"}, + { NULL, 2056, "1"}, + { "RSAPSSParameters", 1610612741, NULL }, + { "hashAlgorithm", 1610637314, "AlgorithmIdentifier"}, + { NULL, 2056, "0"}, + { "maskGenAlgorithm", 1610637314, "AlgorithmIdentifier"}, + { NULL, 2056, "1"}, + { "saltLength", 1610653699, NULL }, + { NULL, 1073741833, "20"}, + { NULL, 2056, "2"}, + { "trailerField", 536911875, NULL }, + { NULL, 1073741833, "1"}, + { NULL, 2056, "3"}, + { "RSAOAEPParameters", 1610612741, NULL }, + { "hashAlgorithm", 1610637314, "AlgorithmIdentifier"}, + { NULL, 2056, "0"}, + { "maskGenAlgorithm", 1610637314, "AlgorithmIdentifier"}, + { NULL, 2056, "1"}, + { "pSourceFunc", 536895490, "AlgorithmIdentifier"}, + { NULL, 2056, "2"}, + { "GOSTParameters", 1610612741, NULL }, + { "publicKeyParamSet", 1073741836, NULL }, + { "digestParamSet", 16396, NULL }, + { "GOSTParametersOld", 1610612741, NULL }, + { "publicKeyParamSet", 1073741836, NULL }, + { "digestParamSet", 1073741836, NULL }, + { "encryptionParamSet", 16396, NULL }, + { "GOSTPrivateKey", 1073741831, NULL }, + { "GOSTPrivateKeyOld", 1073741827, NULL }, + { "IssuerSignTool", 1610612741, NULL }, + { "signTool", 1073741858, NULL }, + { "cATool", 1073741858, NULL }, + { "signToolCert", 1073741858, NULL }, + { "cAToolCert", 34, NULL }, + { "Gost28147-89-EncryptedKey", 1610612741, NULL }, + { "encryptedKey", 1073741831, NULL }, + { "maskKey", 1610637319, NULL }, + { NULL, 4104, "0"}, + { "macKey", 7, NULL }, + { "SubjectPublicKeyInfo", 1610612741, NULL }, + { "algorithm", 1073741826, "AlgorithmIdentifier"}, + { "subjectPublicKey", 6, NULL }, + { "GostR3410-TransportParameters", 1610612741, NULL }, + { "encryptionParamSet", 1073741836, NULL }, + { "ephemeralPublicKey", 1610637314, "SubjectPublicKeyInfo"}, + { NULL, 4104, "0"}, + { "ukm", 7, NULL }, + { "GostR3410-KeyTransport", 1610612741, NULL }, + { "sessionEncryptedKey", 1073741826, "Gost28147-89-EncryptedKey"}, + { "transportParameters", 536895490, "GostR3410-TransportParameters"}, + { NULL, 4104, "0"}, + { "TPMKey", 1610612741, NULL }, + { "type", 1073741836, NULL }, + { "emptyAuth", 1610637316, NULL }, + { NULL, 2056, "0"}, + { "parent", 1073741827, NULL }, + { "pubkey", 1073741831, NULL }, + { "privkey", 7, NULL }, + { "MLDSAPrivateKey", 536870917, NULL }, + { "version", 1073741827, NULL }, + { "privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "privateKey", 1073741831, NULL }, + { "publicKey", 536895495, NULL }, + { NULL, 2056, "1"}, + { NULL, 0, NULL } +}; diff --git a/grub-core/commands/appendedsig/pkix_asn1_tab.c b/grub-core/commands/appendedsig/pkix_asn1_tab.c new file mode 100644 index 000000000..ec5f87bfd --- /dev/null +++ b/grub-core/commands/appendedsig/pkix_asn1_tab.c @@ -0,0 +1,485 @@ +#include +#include + +/* + * Imported from pkix.asn. + * https://github.com/gnutls/gnutls/blob/master/lib/pkix.asn + */ +const asn1_static_node grub_pkix_asn1_tab[] = { + { "PKIX1", 536875024, NULL }, + { NULL, 1073741836, NULL }, + { "PrivateKeyUsagePeriod", 1610612741, NULL }, + { "notBefore", 1610637349, NULL }, + { NULL, 4104, "0"}, + { "notAfter", 536895525, NULL }, + { NULL, 4104, "1"}, + { "AuthorityKeyIdentifier", 1610612741, NULL }, + { "keyIdentifier", 1610637319, NULL }, + { NULL, 4104, "0"}, + { "authorityCertIssuer", 1610637314, "GeneralNames"}, + { NULL, 4104, "1"}, + { "authorityCertSerialNumber", 536895490, "CertificateSerialNumber"}, + { NULL, 4104, "2"}, + { "SubjectKeyIdentifier", 1073741831, NULL }, + { "KeyUsage", 1073741830, NULL }, + { "DirectoryString", 1610612754, NULL }, + { "teletexString", 1612709918, NULL }, + { "MAX", 524298, "1"}, + { "printableString", 1612709919, NULL }, + { "MAX", 524298, "1"}, + { "universalString", 1612709920, NULL }, + { "MAX", 524298, "1"}, + { "utf8String", 1612709922, NULL }, + { "MAX", 524298, "1"}, + { "bmpString", 538968097, NULL }, + { "MAX", 524298, "1"}, + { "SubjectAltName", 1073741826, "GeneralNames"}, + { "GeneralNames", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "GeneralName"}, + { "GeneralName", 1610612754, NULL }, + { "otherName", 1610620930, "AnotherName"}, + { NULL, 4104, "0"}, + { "rfc822Name", 1610620957, NULL }, + { NULL, 4104, "1"}, + { "dNSName", 1610620957, NULL }, + { NULL, 4104, "2"}, + { "x400Address", 1610620941, NULL }, + { NULL, 4104, "3"}, + { "directoryName", 1610620939, NULL }, + { NULL, 1073743880, "4"}, + { NULL, 2, "RelativeDistinguishedName"}, + { "ediPartyName", 1610620941, NULL }, + { NULL, 4104, "5"}, + { "uniformResourceIdentifier", 1610620957, NULL }, + { NULL, 4104, "6"}, + { "iPAddress", 1610620935, NULL }, + { NULL, 4104, "7"}, + { "registeredID", 536879116, NULL }, + { NULL, 4104, "8"}, + { "AnotherName", 1610612741, NULL }, + { "type-id", 1073741836, NULL }, + { "value", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "type-id", 1, NULL }, + { "IssuerAltName", 1073741826, "GeneralNames"}, + { "BasicConstraints", 1610612741, NULL }, + { "cA", 1610645508, NULL }, + { NULL, 131081, NULL }, + { "pathLenConstraint", 16387, NULL }, + { "CRLDistributionPoints", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "DistributionPoint"}, + { "DistributionPoint", 1610612741, NULL }, + { "distributionPoint", 1610637314, "DistributionPointName"}, + { NULL, 2056, "0"}, + { "reasons", 1610637314, "ReasonFlags"}, + { NULL, 4104, "1"}, + { "cRLIssuer", 536895490, "GeneralNames"}, + { NULL, 4104, "2"}, + { "DistributionPointName", 1610612754, NULL }, + { "fullName", 1610620930, "GeneralNames"}, + { NULL, 4104, "0"}, + { "nameRelativeToCRLIssuer", 536879106, "RelativeDistinguishedName"}, + { NULL, 4104, "1"}, + { "ReasonFlags", 1073741830, NULL }, + { "ExtKeyUsageSyntax", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 12, NULL }, + { "AuthorityInfoAccessSyntax", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "AccessDescription"}, + { "AccessDescription", 1610612741, NULL }, + { "accessMethod", 1073741836, NULL }, + { "accessLocation", 2, "GeneralName"}, + { "Attribute", 1610612741, NULL }, + { "type", 1073741836, NULL }, + { "values", 536870927, NULL }, + { NULL, 13, NULL }, + { "AttributeTypeAndValue", 1610612741, NULL }, + { "type", 1073741836, NULL }, + { "value", 13, NULL }, + { "Name", 1610612754, NULL }, + { "rdnSequence", 536870923, NULL }, + { NULL, 2, "RelativeDistinguishedName"}, + { "DistinguishedName", 1610612747, NULL }, + { NULL, 2, "RelativeDistinguishedName"}, + { "RelativeDistinguishedName", 1612709903, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "AttributeTypeAndValue"}, + { "Certificate", 1610612741, NULL }, + { "tbsCertificate", 1073741826, "TBSCertificate"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 6, NULL }, + { "TBSCertificate", 1610612741, NULL }, + { "version", 1610653699, NULL }, + { NULL, 1073741833, "0"}, + { NULL, 2056, "0"}, + { "serialNumber", 1073741826, "CertificateSerialNumber"}, + { "signature", 1073741826, "AlgorithmIdentifier"}, + { "issuer", 1073741826, "Name"}, + { "validity", 1073741826, "Validity"}, + { "subject", 1073741826, "Name"}, + { "subjectPublicKeyInfo", 1073741826, "SubjectPublicKeyInfo"}, + { "issuerUniqueID", 1610637314, "UniqueIdentifier"}, + { NULL, 4104, "1"}, + { "subjectUniqueID", 1610637314, "UniqueIdentifier"}, + { NULL, 4104, "2"}, + { "extensions", 536895490, "Extensions"}, + { NULL, 2056, "3"}, + { "CertificateSerialNumber", 1073741827, NULL }, + { "Validity", 1610612741, NULL }, + { "notBefore", 1073741826, "Time"}, + { "notAfter", 2, "Time"}, + { "Time", 1610612754, NULL }, + { "utcTime", 1073741860, NULL }, + { "generalTime", 37, NULL }, + { "UniqueIdentifier", 1073741830, NULL }, + { "SubjectPublicKeyInfo", 1610612741, NULL }, + { "algorithm", 1073741826, "AlgorithmIdentifier"}, + { "subjectPublicKey", 6, NULL }, + { "Extensions", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "Extension"}, + { "Extension", 1610612741, NULL }, + { "extnID", 1073741836, NULL }, + { "critical", 1610645508, NULL }, + { NULL, 131081, NULL }, + { "extnValue", 7, NULL }, + { "CertificateList", 1610612741, NULL }, + { "tbsCertList", 1073741826, "TBSCertList"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 6, NULL }, + { "TBSCertList", 1610612741, NULL }, + { "version", 1073758211, NULL }, + { "signature", 1073741826, "AlgorithmIdentifier"}, + { "issuer", 1073741826, "Name"}, + { "thisUpdate", 1073741826, "Time"}, + { "nextUpdate", 1073758210, "Time"}, + { "revokedCertificates", 1610629131, NULL }, + { NULL, 536870917, NULL }, + { "userCertificate", 1073741826, "CertificateSerialNumber"}, + { "revocationDate", 1073741826, "Time"}, + { "crlEntryExtensions", 16386, "Extensions"}, + { "crlExtensions", 536895490, "Extensions"}, + { NULL, 2056, "0"}, + { "AlgorithmIdentifier", 1610612741, NULL }, + { "algorithm", 1073741836, NULL }, + { "parameters", 541081613, NULL }, + { "algorithm", 1, NULL }, + { "Dss-Sig-Value", 1610612741, NULL }, + { "r", 1073741827, NULL }, + { "s", 3, NULL }, + { "Dss-Parms", 1610612741, NULL }, + { "p", 1073741827, NULL }, + { "q", 1073741827, NULL }, + { "g", 3, NULL }, + { "pkcs-7-ContentInfo", 1610612741, NULL }, + { "contentType", 1073741836, NULL }, + { "content", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "contentType", 1, NULL }, + { "pkcs-7-DigestInfo", 1610612741, NULL }, + { "digestAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "digest", 7, NULL }, + { "pkcs-7-SignedData", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "digestAlgorithms", 1073741826, "pkcs-7-DigestAlgorithmIdentifiers"}, + { "encapContentInfo", 1073741826, "pkcs-7-EncapsulatedContentInfo"}, + { "certificates", 1610637314, "pkcs-7-CertificateSet"}, + { NULL, 4104, "0"}, + { "crls", 1610637314, "pkcs-7-CertificateRevocationLists"}, + { NULL, 4104, "1"}, + { "signerInfos", 2, "pkcs-7-SignerInfos"}, + { "pkcs-7-DigestAlgorithmIdentifiers", 1610612751, NULL }, + { NULL, 2, "AlgorithmIdentifier"}, + { "pkcs-7-EncapsulatedContentInfo", 1610612741, NULL }, + { "eContentType", 1073741836, NULL }, + { "eContent", 536895501, NULL }, + { NULL, 2056, "0"}, + { "pkcs-7-CertificateRevocationLists", 1610612751, NULL }, + { NULL, 13, NULL }, + { "pkcs-7-CertificateChoices", 1610612754, NULL }, + { "certificate", 13, NULL }, + { "pkcs-7-CertificateSet", 1610612751, NULL }, + { NULL, 2, "pkcs-7-CertificateChoices"}, + { "IssuerAndSerialNumber", 1610612741, NULL }, + { "issuer", 1073741826, "Name"}, + { "serialNumber", 2, "CertificateSerialNumber"}, + { "pkcs-7-SignerInfo", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "sid", 1073741826, "SignerIdentifier"}, + { "digestAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signedAttrs", 1610637314, "SignedAttributes"}, + { NULL, 4104, "0"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 1073741831, NULL }, + { "unsignedAttrs", 536895490, "SignedAttributes"}, + { NULL, 4104, "1"}, + { "SignedAttributes", 1612709903, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "Attribute"}, + { "SignerIdentifier", 1610612754, NULL }, + { "issuerAndSerialNumber", 1073741826, "IssuerAndSerialNumber"}, + { "subjectKeyIdentifier", 536879111, NULL }, + { NULL, 4104, "0"}, + { "pkcs-7-SignerInfos", 1610612751, NULL }, + { NULL, 2, "pkcs-7-SignerInfo"}, + { "pkcs-10-CertificationRequestInfo", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "subject", 1073741826, "Name"}, + { "subjectPKInfo", 1073741826, "SubjectPublicKeyInfo"}, + { "attributes", 536879106, "Attributes"}, + { NULL, 4104, "0"}, + { "Attributes", 1610612751, NULL }, + { NULL, 2, "Attribute"}, + { "pkcs-10-CertificationRequest", 1610612741, NULL }, + { "certificationRequestInfo", 1073741826, "pkcs-10-CertificationRequestInfo"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 6, NULL }, + { "pkcs-9-at-challengePassword", 1879048204, NULL }, + { "iso", 1073741825, "1"}, + { "member-body", 1073741825, "2"}, + { "us", 1073741825, "840"}, + { "rsadsi", 1073741825, "113549"}, + { "pkcs", 1073741825, "1"}, + { NULL, 1073741825, "9"}, + { NULL, 1, "7"}, + { "pkcs-9-challengePassword", 1610612754, NULL }, + { "printableString", 1073741855, NULL }, + { "utf8String", 34, NULL }, + { "pkcs-9-localKeyId", 1073741831, NULL }, + { "pkcs-8-PrivateKeyInfo", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "privateKey", 1073741831, NULL }, + { "attributes", 536895490, "Attributes"}, + { NULL, 4104, "0"}, + { "pkcs-8-EncryptedPrivateKeyInfo", 1610612741, NULL }, + { "encryptionAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "encryptedData", 2, "pkcs-8-EncryptedData"}, + { "pkcs-8-EncryptedData", 1073741831, NULL }, + { "pkcs-5-des-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "8"}, + { "pkcs-5-des-EDE3-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "8"}, + { "pkcs-5-aes128-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "16"}, + { "pkcs-5-aes192-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "16"}, + { "pkcs-5-aes256-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "16"}, + { "Gost28147-89-Parameters", 1610612741, NULL }, + { "iv", 1073741831, NULL }, + { "encryptionParamSet", 12, NULL }, + { "pkcs-5-PBE-params", 1610612741, NULL }, + { "salt", 1073741831, NULL }, + { "iterationCount", 3, NULL }, + { "pkcs-5-PBES2-params", 1610612741, NULL }, + { "keyDerivationFunc", 1073741826, "AlgorithmIdentifier"}, + { "encryptionScheme", 2, "AlgorithmIdentifier"}, + { "pkcs-5-PBMAC1-params", 1610612741, NULL }, + { "keyDerivationFunc", 1073741826, "AlgorithmIdentifier"}, + { "messageAuthScheme", 2, "AlgorithmIdentifier"}, + { "pkcs-5-PBKDF2-params", 1610612741, NULL }, + { "salt", 1610612754, NULL }, + { "specified", 1073741831, NULL }, + { "otherSource", 2, "AlgorithmIdentifier"}, + { "iterationCount", 1073741827, NULL }, + { "keyLength", 1073758211, NULL }, + { "prf", 16386, "AlgorithmIdentifier"}, + { "pkcs-12-PFX", 1610612741, NULL }, + { "version", 1610874883, NULL }, + { "v3", 1, "3"}, + { "authSafe", 1073741826, "pkcs-7-ContentInfo"}, + { "macData", 16386, "pkcs-12-MacData"}, + { "pkcs-12-PbeParams", 1610612741, NULL }, + { "salt", 1073741831, NULL }, + { "iterations", 3, NULL }, + { "pkcs-12-MacData", 1610612741, NULL }, + { "mac", 1073741826, "pkcs-7-DigestInfo"}, + { "macSalt", 1073741831, NULL }, + { "iterations", 536903683, NULL }, + { NULL, 9, "1"}, + { "pkcs-12-AuthenticatedSafe", 1610612747, NULL }, + { NULL, 2, "pkcs-7-ContentInfo"}, + { "pkcs-12-SafeContents", 1610612747, NULL }, + { NULL, 2, "pkcs-12-SafeBag"}, + { "pkcs-12-SafeBag", 1610612741, NULL }, + { "bagId", 1073741836, NULL }, + { "bagValue", 1614815245, NULL }, + { NULL, 1073743880, "0"}, + { "badId", 1, NULL }, + { "bagAttributes", 536887311, NULL }, + { NULL, 2, "Attribute"}, + { "pkcs-12-CertBag", 1610612741, NULL }, + { "certId", 1073741836, NULL }, + { "certValue", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "certId", 1, NULL }, + { "pkcs-12-CRLBag", 1610612741, NULL }, + { "crlId", 1073741836, NULL }, + { "crlValue", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "crlId", 1, NULL }, + { "pkcs-12-SecretBag", 1610612741, NULL }, + { "secretTypeId", 1073741836, NULL }, + { "secretValue", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "secretTypeId", 1, NULL }, + { "pkcs-7-Data", 1073741831, NULL }, + { "pkcs-7-EncryptedData", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "encryptedContentInfo", 1073741826, "pkcs-7-EncryptedContentInfo"}, + { "unprotectedAttrs", 536895490, "pkcs-7-UnprotectedAttributes"}, + { NULL, 4104, "1"}, + { "pkcs-7-EncryptedContentInfo", 1610612741, NULL }, + { "contentType", 1073741836, NULL }, + { "contentEncryptionAlgorithm", 1073741826, "pkcs-7-ContentEncryptionAlgorithmIdentifier"}, + { "encryptedContent", 536895495, NULL }, + { NULL, 4104, "0"}, + { "pkcs-7-ContentEncryptionAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, + { "pkcs-7-UnprotectedAttributes", 1612709903, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "Attribute"}, + { "ProxyCertInfo", 1610612741, NULL }, + { "pCPathLenConstraint", 1073758211, NULL }, + { "proxyPolicy", 2, "ProxyPolicy"}, + { "ProxyPolicy", 1610612741, NULL }, + { "policyLanguage", 1073741836, NULL }, + { "policy", 16391, NULL }, + { "certificatePolicies", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "PolicyInformation"}, + { "PolicyInformation", 1610612741, NULL }, + { "policyIdentifier", 1073741836, NULL }, + { "policyQualifiers", 538984459, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "PolicyQualifierInfo"}, + { "PolicyQualifierInfo", 1610612741, NULL }, + { "policyQualifierId", 1073741836, NULL }, + { "qualifier", 541065229, NULL }, + { "policyQualifierId", 1, NULL }, + { "CPSuri", 1073741853, NULL }, + { "UserNotice", 1610612741, NULL }, + { "noticeRef", 1073758210, "NoticeReference"}, + { "explicitText", 16386, "DisplayText"}, + { "NoticeReference", 1610612741, NULL }, + { "organization", 1073741826, "DisplayText"}, + { "noticeNumbers", 536870923, NULL }, + { NULL, 3, NULL }, + { "DisplayText", 1610612754, NULL }, + { "ia5String", 1612709917, NULL }, + { "200", 524298, "1"}, + { "visibleString", 1612709923, NULL }, + { "200", 524298, "1"}, + { "bmpString", 1612709921, NULL }, + { "200", 524298, "1"}, + { "utf8String", 538968098, NULL }, + { "200", 524298, "1"}, + { "OCSPRequest", 1610612741, NULL }, + { "tbsRequest", 1073741826, "TBSRequest"}, + { "optionalSignature", 536895490, "Signature"}, + { NULL, 2056, "0"}, + { "TBSRequest", 1610612741, NULL }, + { "version", 1610653699, NULL }, + { NULL, 1073741833, "0"}, + { NULL, 2056, "0"}, + { "requestorName", 1610637314, "GeneralName"}, + { NULL, 2056, "1"}, + { "requestList", 1610612747, NULL }, + { NULL, 2, "Request"}, + { "requestExtensions", 536895490, "Extensions"}, + { NULL, 2056, "2"}, + { "Signature", 1610612741, NULL }, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 1073741830, NULL }, + { "certs", 536895499, NULL }, + { NULL, 1073743880, "0"}, + { NULL, 2, "Certificate"}, + { "Request", 1610612741, NULL }, + { "reqCert", 1073741826, "CertID"}, + { "singleRequestExtensions", 536895490, "Extensions"}, + { NULL, 2056, "0"}, + { "CertID", 1610612741, NULL }, + { "hashAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "issuerNameHash", 1073741831, NULL }, + { "issuerKeyHash", 1073741831, NULL }, + { "serialNumber", 2, "CertificateSerialNumber"}, + { "OCSPResponse", 1610612741, NULL }, + { "responseStatus", 1073741826, "OCSPResponseStatus"}, + { "responseBytes", 536895490, "ResponseBytes"}, + { NULL, 2056, "0"}, + { "OCSPResponseStatus", 1610874901, NULL }, + { "successful", 1073741825, "0"}, + { "malformedRequest", 1073741825, "1"}, + { "internalError", 1073741825, "2"}, + { "tryLater", 1073741825, "3"}, + { "sigRequired", 1073741825, "5"}, + { "unauthorized", 1, "6"}, + { "ResponseBytes", 1610612741, NULL }, + { "responseType", 1073741836, NULL }, + { "response", 7, NULL }, + { "BasicOCSPResponse", 1610612741, NULL }, + { "tbsResponseData", 1073741826, "ResponseData"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 1073741830, NULL }, + { "certs", 536895499, NULL }, + { NULL, 1073743880, "0"}, + { NULL, 2, "Certificate"}, + { "ResponseData", 1610612741, NULL }, + { "version", 1610653699, NULL }, + { NULL, 1073741833, "0"}, + { NULL, 2056, "0"}, + { "responderID", 1073741826, "ResponderID"}, + { "producedAt", 1073741861, NULL }, + { "responses", 1610612747, NULL }, + { NULL, 2, "SingleResponse"}, + { "responseExtensions", 536895490, "Extensions"}, + { NULL, 2056, "1"}, + { "ResponderID", 1610612754, NULL }, + { "byName", 1610620939, NULL }, + { NULL, 1073743880, "1"}, + { NULL, 2, "RelativeDistinguishedName"}, + { "byKey", 536879111, NULL }, + { NULL, 2056, "2"}, + { "SingleResponse", 1610612741, NULL }, + { "certID", 1073741826, "CertID"}, + { "certStatus", 1073741826, "CertStatus"}, + { "thisUpdate", 1073741861, NULL }, + { "nextUpdate", 1610637349, NULL }, + { NULL, 2056, "0"}, + { "singleExtensions", 536895490, "Extensions"}, + { NULL, 2056, "1"}, + { "CertStatus", 1610612754, NULL }, + { "good", 1610620948, NULL }, + { NULL, 4104, "0"}, + { "revoked", 1610620930, "RevokedInfo"}, + { NULL, 4104, "1"}, + { "unknown", 536879106, "UnknownInfo"}, + { NULL, 4104, "2"}, + { "RevokedInfo", 1610612741, NULL }, + { "revocationTime", 1073741861, NULL }, + { "revocationReason", 537157653, NULL }, + { NULL, 1073743880, "0"}, + { "unspecified", 1, "0"}, + { "UnknownInfo", 1073741844, NULL }, + { "NameConstraints", 1610612741, NULL }, + { "permittedSubtrees", 1610637314, "GeneralSubtrees"}, + { NULL, 4104, "0"}, + { "excludedSubtrees", 536895490, "GeneralSubtrees"}, + { NULL, 4104, "1"}, + { "GeneralSubtrees", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "GeneralSubtree"}, + { "GeneralSubtree", 1610612741, NULL }, + { "base", 1073741826, "GeneralName"}, + { "minimum", 1610653699, NULL }, + { NULL, 1073741833, "0"}, + { NULL, 4104, "0"}, + { "maximum", 536895491, NULL }, + { NULL, 4104, "1"}, + { "TlsFeatures", 536870923, NULL }, + { NULL, 3, NULL }, + { NULL, 0, NULL } +}; From 3e4ff6ffb377608de2daa0ee4ff5ac896c0d021f Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:54:51 +0530 Subject: [PATCH 038/208] appended signatures: Parse ASN1 node This code allows us to parse ASN1 node and allocating memory to store it. It will work for anything where the size libtasn1 returns is right: - Integers - Octet strings - DER encoding of other structures It will _not_ work for things where libtasn1 size requires adjustment: - Strings that require an extra NULL byte at the end - Bit strings because libtasn1 returns the length in bits, not bytes. If the function returns a non-NULL value, the caller must free it. Signed-off-by: Daniel Axtens Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Stefan Berger Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/commands/appendedsig/appendedsig.h | 44 +++++++++ grub-core/commands/appendedsig/asn1util.c | 99 ++++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 grub-core/commands/appendedsig/appendedsig.h create mode 100644 grub-core/commands/appendedsig/asn1util.c diff --git a/grub-core/commands/appendedsig/appendedsig.h b/grub-core/commands/appendedsig/appendedsig.h new file mode 100644 index 000000000..601d6164f --- /dev/null +++ b/grub-core/commands/appendedsig/appendedsig.h @@ -0,0 +1,44 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020, 2022 Free Software Foundation, Inc. + * Copyright (C) 2020, 2022, 2025 IBM Corporation + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include + +extern asn1_node grub_gnutls_gnutls_asn; +extern asn1_node grub_gnutls_pkix_asn; + +/* Do libtasn1 init. */ +extern int +grub_asn1_init (void); + +/* + * Read a value from an ASN1 node, allocating memory to store it. It will work + * for anything where the size libtasn1 returns is right: + * - Integers + * - Octet strings + * - DER encoding of other structures + * + * It will _not_ work for things where libtasn1 size requires adjustment: + * - Strings that require an extra null byte at the end + * - Bit strings because libtasn1 returns the length in bits, not bytes. + * + * If the function returns a non-NULL value, the caller must free it. + */ +extern void * +grub_asn1_allocate_and_read (asn1_node node, const char *name, const char *friendly_name, + grub_int32_t *content_size); diff --git a/grub-core/commands/appendedsig/asn1util.c b/grub-core/commands/appendedsig/asn1util.c new file mode 100644 index 000000000..9dd7898ea --- /dev/null +++ b/grub-core/commands/appendedsig/asn1util.c @@ -0,0 +1,99 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020, 2022 Free Software Foundation, Inc. + * Copyright (C) 2020, 2022, 2025 IBM Corporation + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "appendedsig.h" + +asn1_node grub_gnutls_gnutls_asn = NULL; +asn1_node grub_gnutls_pkix_asn = NULL; + +extern const asn1_static_node grub_gnutls_asn1_tab[]; +extern const asn1_static_node grub_pkix_asn1_tab[]; + +/* + * Read a value from an ASN1 node, allocating memory to store it. It will work + * for anything where the size libtasn1 returns is right: + * - Integers + * - Octet strings + * - DER encoding of other structures + * + * It will _not_ work for things where libtasn1 size requires adjustment: + * - Strings that require an extra NULL byte at the end + * - Bit strings because libtasn1 returns the length in bits, not bytes. + * + * If the function returns a non-NULL value, the caller must free it. + */ +void * +grub_asn1_allocate_and_read (asn1_node node, const char *name, const char *friendly_name, + grub_int32_t *content_size) +{ + grub_int32_t result; + grub_uint8_t *tmpstr = NULL; + grub_int32_t tmpstr_size = 0; + + result = asn1_read_value (node, name, NULL, &tmpstr_size); + if (result != ASN1_MEM_ERROR) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, "reading size of %s did not return expected status: %s", + friendly_name, asn1_strerror (result)) ; + return NULL; + } + + tmpstr = grub_malloc (tmpstr_size); + if (tmpstr == NULL) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "could not allocate memory to store %s", + friendly_name) ; + return NULL; + } + + result = asn1_read_value (node, name, tmpstr, &tmpstr_size); + if (result != ASN1_SUCCESS) + { + grub_free (tmpstr); + grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading %s: %s", friendly_name, + asn1_strerror (result)) ; + return NULL; + } + + *content_size = tmpstr_size; + + return tmpstr; +} + +int +grub_asn1_init (void) +{ + int res; + + res = asn1_array2tree (grub_gnutls_asn1_tab, &grub_gnutls_gnutls_asn, NULL); + if (res != ASN1_SUCCESS) + return res; + + res = asn1_array2tree (grub_pkix_asn1_tab, &grub_gnutls_pkix_asn, NULL); + + return res; +} From a33754979725746d7d5b3809089e519006d0d4c3 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:54:52 +0530 Subject: [PATCH 039/208] appended signatures: Parse PKCS#7 signed data This code allows us to parse: - PKCS#7 signed data messages. Only a single signer info is supported, which is all that the Linux sign-file utility supports creating out-of-the-box. Only RSA, SHA-256 and SHA-512 are supported. Any certificate embedded in the PKCS#7 message will be ignored. Signed-off-by: Daniel Axtens Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/commands/appendedsig/appendedsig.h | 37 ++ grub-core/commands/appendedsig/pkcs7.c | 452 +++++++++++++++++++ 2 files changed, 489 insertions(+) create mode 100644 grub-core/commands/appendedsig/pkcs7.c diff --git a/grub-core/commands/appendedsig/appendedsig.h b/grub-core/commands/appendedsig/appendedsig.h index 601d6164f..b0beb8935 100644 --- a/grub-core/commands/appendedsig/appendedsig.h +++ b/grub-core/commands/appendedsig/appendedsig.h @@ -17,11 +17,48 @@ * along with GRUB. If not, see . */ +#include #include extern asn1_node grub_gnutls_gnutls_asn; extern asn1_node grub_gnutls_pkix_asn; +#define GRUB_MAX_OID_LEN 32 + +/* A PKCS#7 signed data signer info. */ +struct pkcs7_signer +{ + const gcry_md_spec_t *hash; + gcry_mpi_t sig_mpi; +}; +typedef struct pkcs7_signer grub_pkcs7_signer_t; + +/* + * A PKCS#7 signed data message. We make no attempt to match intelligently, so + * we don't save any info about the signer. + */ +struct pkcs7_data +{ + grub_int32_t signer_count; + grub_pkcs7_signer_t *signers; +}; +typedef struct pkcs7_data grub_pkcs7_data_t; + +/* + * Parse a PKCS#7 message, which must be a signed data message. The message must + * be in 'sigbuf' and of size 'data_size'. The result is placed in 'msg', which + * must already be allocated. + */ +extern grub_err_t +grub_pkcs7_data_parse (const void *sigbuf, grub_size_t data_size, grub_pkcs7_data_t *msg); + +/* + * Release all the storage associated with the PKCS#7 message. If the caller + * dynamically allocated the message, it must free it. + */ +extern void +grub_pkcs7_data_release (grub_pkcs7_data_t *msg); + /* Do libtasn1 init. */ extern int grub_asn1_init (void); diff --git a/grub-core/commands/appendedsig/pkcs7.c b/grub-core/commands/appendedsig/pkcs7.c new file mode 100644 index 000000000..b8e272047 --- /dev/null +++ b/grub-core/commands/appendedsig/pkcs7.c @@ -0,0 +1,452 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020, 2022 Free Software Foundation, Inc. + * Copyright (C) 2020, 2022, 2025 IBM Corporation + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include "appendedsig.h" +#include +#include +#include +#include + +static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + +/* RFC 5652 s 5.1. */ +static const char *signedData_oid = "1.2.840.113549.1.7.2"; + +/* RFC 4055 s 2.1. */ +static const char *sha256_oid = "2.16.840.1.101.3.4.2.1"; +static const char *sha512_oid = "2.16.840.1.101.3.4.2.3"; + +static grub_err_t +process_content (grub_uint8_t *content, grub_int32_t size, grub_pkcs7_data_t *msg) +{ + grub_int32_t res; + asn1_node signed_part; + grub_err_t err = GRUB_ERR_NONE; + char algo_oid[GRUB_MAX_OID_LEN]; + grub_int32_t algo_oid_size; + grub_int32_t algo_count; + grub_int32_t signer_count; + grub_int32_t i; + char version; + grub_int32_t version_size = sizeof (version); + grub_uint8_t *result_buf; + grub_int32_t result_size = 0; + grub_int32_t crls_size = 0; + gcry_error_t gcry_err; + bool sha256_in_da, sha256_in_si, sha512_in_da, sha512_in_si; + char *da_path; + char *si_sig_path; + char *si_da_path; + + res = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.pkcs-7-SignedData", &signed_part); + if (res != ASN1_SUCCESS) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not create ASN.1 structure for PKCS#7 signed part"); + + res = asn1_der_decoding2 (&signed_part, content, &size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (res != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, + "error reading PKCS#7 signed data: %s", asn1_error); + goto cleanup_signed_part; + } + + /* + * SignedData ::= SEQUENCE { + * version CMSVersion, + * digestAlgorithms DigestAlgorithmIdentifiers, + * encapContentInfo EncapsulatedContentInfo, + * certificates [0] IMPLICIT CertificateSet OPTIONAL, + * crls [1] IMPLICIT RevocationInfoChoices OPTIONAL, + * signerInfos SignerInfos } + */ + + res = asn1_read_value (signed_part, "version", &version, &version_size); + if (res != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, "error reading signedData version: %s", + asn1_strerror (res)); + goto cleanup_signed_part; + } + + /* Signature version must be 1 because appended signature only support v1. */ + if (version != 1) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, + "unexpected signature version v%d, only v1 supported", version); + goto cleanup_signed_part; + } + + /* + * digestAlgorithms DigestAlgorithmIdentifiers + * + * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier + * DigestAlgorithmIdentifer is an X.509 AlgorithmIdentifier (10.1.1) + * + * RFC 4055 s 2.1: + * sha256Identifier AlgorithmIdentifier ::= { id-sha256, NULL } + * sha512Identifier AlgorithmIdentifier ::= { id-sha512, NULL } + * + * We only support 1 element in the set, and we do not check parameters atm. + */ + res = asn1_number_of_elements (signed_part, "digestAlgorithms", &algo_count); + if (res != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, "error counting number of digest algorithms: %s", + asn1_strerror (res)); + goto cleanup_signed_part; + } + + if (algo_count <= 0) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, "a minimum of 1 digest algorithm is required"); + goto cleanup_signed_part; + } + + if (algo_count > 2) + { + err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "a maximum of 2 digest algorithms is supported"); + goto cleanup_signed_part; + } + + sha256_in_da = false; + sha512_in_da = false; + + for (i = 0; i < algo_count; i++) + { + da_path = grub_xasprintf ("digestAlgorithms.?%d.algorithm", i + 1); + if (da_path == NULL) + { + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not allocate path for digest algorithm parsing path"); + goto cleanup_signed_part; + } + + algo_oid_size = sizeof (algo_oid); + res = asn1_read_value (signed_part, da_path, algo_oid, &algo_oid_size); + if (res != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, "error reading digest algorithm: %s", + asn1_strerror (res)); + grub_free (da_path); + goto cleanup_signed_part; + } + + if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0) + { + if (sha512_in_da == false) + sha512_in_da = true; + else + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, + "SHA-512 specified twice in digest algorithm list"); + grub_free (da_path); + goto cleanup_signed_part; + } + } + else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0) + { + if (sha256_in_da == false) + sha256_in_da = true; + else + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, + "SHA-256 specified twice in digest algorithm list"); + grub_free (da_path); + goto cleanup_signed_part; + } + } + else + { + err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "only SHA-256 and SHA-512 hashes are supported, found OID %s", + algo_oid); + grub_free (da_path); + goto cleanup_signed_part; + } + + grub_free (da_path); + } + + /* At this point, at least one of sha{256,512}_in_da must be true. */ + + /* + * We ignore the certificates, but we don't permit CRLs. A CRL entry might be + * revoking the certificate we're using, and we have no way of dealing with + * that at the moment. + */ + res = asn1_read_value (signed_part, "crls", NULL, &crls_size); + if (res != ASN1_ELEMENT_NOT_FOUND) + { + err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "PKCS#7 messages with embedded CRLs are not supported"); + goto cleanup_signed_part; + } + + /* Read the signatures */ + res = asn1_number_of_elements (signed_part, "signerInfos", &signer_count); + if (res != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, "error counting number of signers: %s", + asn1_strerror (res)); + goto cleanup_signed_part; + } + + if (signer_count <= 0) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, "a minimum of 1 signer is required"); + goto cleanup_signed_part; + } + + msg->signers = grub_calloc (signer_count, sizeof (grub_pkcs7_signer_t)); + if (msg->signers == NULL) + { + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not allocate space for %d signers", signer_count); + goto cleanup_signed_part; + } + + msg->signer_count = 0; + for (i = 0; i < signer_count; i++) + { + si_da_path = grub_xasprintf ("signerInfos.?%d.digestAlgorithm.algorithm", i + 1); + if (si_da_path == NULL) + { + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not allocate path for signer %d's digest algorithm parsing path", + i); + goto cleanup_signerInfos; + } + + algo_oid_size = sizeof (algo_oid); + res = asn1_read_value (signed_part, si_da_path, algo_oid, &algo_oid_size); + if (res != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, + "error reading signer %d's digest algorithm: %s", i, asn1_strerror (res)); + grub_free (si_da_path); + goto cleanup_signerInfos; + } + + grub_free (si_da_path); + + if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0) + { + if (sha512_in_da == false) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, + "signer %d claims a SHA-512 signature which was not " + "specified in the outer DigestAlgorithms", i); + goto cleanup_signerInfos; + } + else + { + sha512_in_si = true; + msg->signers[i].hash = grub_crypto_lookup_md_by_name ("sha512"); + } + } + else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0) + { + if (sha256_in_da == false) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, + "signer %d claims a SHA-256 signature which was not " + "specified in the outer DigestAlgorithms", i); + goto cleanup_signerInfos; + } + else + { + sha256_in_si = true; + msg->signers[i].hash = grub_crypto_lookup_md_by_name ("sha256"); + } + } + else + { + err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "only SHA-256 and SHA-512 hashes are supported, found OID %s", + algo_oid); + goto cleanup_signerInfos; + } + + if (msg->signers[i].hash == NULL) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, + "Hash algorithm for signer %d (OID %s) not loaded", i, algo_oid); + goto cleanup_signerInfos; + } + + si_sig_path = grub_xasprintf ("signerInfos.?%d.signature", i + 1); + if (si_sig_path == NULL) + { + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not allocate path for signer %d's signature parsing path", i); + goto cleanup_signerInfos; + } + + result_buf = grub_asn1_allocate_and_read (signed_part, si_sig_path, "signature data", &result_size); + grub_free (si_sig_path); + + if (result_buf == NULL) + { + err = grub_errno; + goto cleanup_signerInfos; + } + + gcry_err = _gcry_mpi_scan (&(msg->signers[i].sig_mpi), GCRYMPI_FMT_USG, + result_buf, result_size, NULL); + grub_free (result_buf); + + if (gcry_err != GPG_ERR_NO_ERROR) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, + "error loading signature %d into MPI structure: %d", + i, gcry_err); + goto cleanup_signerInfos; + } + + /* + * Use msg->signer_count to track fully populated signerInfos so we know + * how many we need to clean up. + */ + msg->signer_count++; + } + + /* + * Final consistency check of signerInfo.*.digestAlgorithm vs digestAlgorithms + * .*.algorithm. An algorithm must be present in both digestAlgorithms and + * signerInfo or in neither. We have already checked for an algorithm in + * signerInfo that is not in digestAlgorithms, here we check for algorithms in + * digestAlgorithms but not in signerInfos. + */ + if (sha512_in_da == true && sha512_in_si == false) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, + "SHA-512 specified in DigestAlgorithms but did not appear in SignerInfos"); + goto cleanup_signerInfos; + } + + if (sha256_in_da == true && sha256_in_si == false) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, + "SHA-256 specified in DigestAlgorithms but did not appear in SignerInfos"); + goto cleanup_signerInfos; + } + + asn1_delete_structure (&signed_part); + + return GRUB_ERR_NONE; + + cleanup_signerInfos: + for (i = 0; i < msg->signer_count; i++) + _gcry_mpi_release (msg->signers[i].sig_mpi); + + grub_free (msg->signers); + + cleanup_signed_part: + asn1_delete_structure (&signed_part); + + return err; +} + +grub_err_t +grub_pkcs7_data_parse (const void *sigbuf, grub_size_t data_size, grub_pkcs7_data_t *msg) +{ + grub_int32_t res; + asn1_node content_info; + grub_err_t err = GRUB_ERR_NONE; + char content_oid[GRUB_MAX_OID_LEN]; + grub_uint8_t *content; + grub_int32_t content_size; + grub_int32_t content_oid_size = sizeof (content_oid); + grub_int32_t size = (grub_int32_t) data_size; + + if (data_size > GRUB_UINT_MAX) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "cannot parse a PKCS#7 message where data size > GRUB_UINT_MAX"); + + res = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.pkcs-7-ContentInfo", &content_info); + if (res != ASN1_SUCCESS) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not create ASN.1 structure for PKCS#7 data: %s", + asn1_strerror (res)); + + res = asn1_der_decoding2 (&content_info, sigbuf, &size, + ASN1_DECODE_FLAG_STRICT_DER | ASN1_DECODE_FLAG_ALLOW_PADDING, + asn1_error); + if (res != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, + "error decoding PKCS#7 message DER: %s", asn1_error); + goto cleanup; + } + + /* + * ContentInfo ::= SEQUENCE { + * contentType ContentType, + * content [0] EXPLICIT ANY DEFINED BY contentType } + * + * ContentType ::= OBJECT IDENTIFIER + */ + res = asn1_read_value (content_info, "contentType", content_oid, &content_oid_size); + if (res != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, "error reading PKCS#7 content type: %s", + asn1_strerror (res)); + goto cleanup; + } + + /* OID for SignedData defined in 5.1. */ + if (grub_strncmp (signedData_oid, content_oid, content_oid_size) != 0) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, + "unexpected content type in PKCS#7 message: OID %s", content_oid); + goto cleanup; + } + + content = grub_asn1_allocate_and_read (content_info, "content", "PKCS#7 message content", &content_size); + if (content == NULL) + { + err = grub_errno; + goto cleanup; + } + + err = process_content (content, content_size, msg); + grub_free (content); + + cleanup: + asn1_delete_structure (&content_info); + + return err; +} + +/* + * Release all the storage associated with the PKCS#7 message. If the caller + * dynamically allocated the message, it must free it. + */ +void +grub_pkcs7_data_release (grub_pkcs7_data_t *msg) +{ + grub_int32_t i; + + for (i = 0; i < msg->signer_count; i++) + _gcry_mpi_release (msg->signers[i].sig_mpi); + + grub_free (msg->signers); +} From e95c52f1f48baf98cb64a204b681bee23265099f Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:54:53 +0530 Subject: [PATCH 040/208] appended signatures: Parse X.509 certificates This code allows us to parse: - X.509 certificates: at least enough to verify the signatures on the PKCS#7 messages. We expect that the certificates embedded in GRUB will be leaf certificates, not CA certificates. The parser enforces this. - X.509 certificates support the Extended Key Usage extension and handle it by verifying that the certificate has a Code Signing usage. Signed-off-by: Javier Martinez Canillas # EKU support Reported-by: Michal Suchanek # key usage issue Signed-off-by: Daniel Axtens Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/commands/appendedsig/appendedsig.h | 52 + grub-core/commands/appendedsig/x509.c | 970 +++++++++++++++++++ include/grub/crypto.h | 1 + 3 files changed, 1023 insertions(+) create mode 100644 grub-core/commands/appendedsig/x509.c diff --git a/grub-core/commands/appendedsig/appendedsig.h b/grub-core/commands/appendedsig/appendedsig.h index b0beb8935..c8746544e 100644 --- a/grub-core/commands/appendedsig/appendedsig.h +++ b/grub-core/commands/appendedsig/appendedsig.h @@ -25,6 +25,43 @@ extern asn1_node grub_gnutls_pkix_asn; #define GRUB_MAX_OID_LEN 32 +/* RSA public key. */ +#define GRUB_MAX_MPI 2 +#define GRUB_RSA_PK_MODULUS 0 +#define GRUB_RSA_PK_EXPONENT 1 + +/* Certificate fingerprint. */ +#define GRUB_MAX_FINGERPRINT 3 +#define GRUB_FINGERPRINT_SHA256 0 +#define GRUB_FINGERPRINT_SHA384 1 +#define GRUB_FINGERPRINT_SHA512 2 + +/* Max size of hash data. */ +#define GRUB_MAX_HASH_LEN 64 + +/* + * One or more x509 certificates. We do limited parsing: + * extracting only the version, serial, issuer, subject, RSA public key + * and key size. + * Also, hold the sha256, sha384, and sha512 fingerprint of the certificate. + */ +struct x509_certificate +{ + struct x509_certificate *next; + grub_uint8_t version; + grub_uint8_t *serial; + grub_size_t serial_len; + char *issuer; + grub_size_t issuer_len; + char *subject; + grub_size_t subject_len; + /* We only support RSA public keys. This encodes [modulus, publicExponent]. */ + gcry_mpi_t mpis[GRUB_MAX_MPI]; + grub_int32_t modulus_size; + grub_uint8_t fingerprint[GRUB_MAX_FINGERPRINT][GRUB_MAX_HASH_LEN]; +}; +typedef struct x509_certificate grub_x509_cert_t; + /* A PKCS#7 signed data signer info. */ struct pkcs7_signer { @@ -44,6 +81,21 @@ struct pkcs7_data }; typedef struct pkcs7_data grub_pkcs7_data_t; +/* + * Import a DER-encoded certificate at 'data', of size 'size'. Place the results + * into 'results', which must be already allocated. + */ +extern grub_err_t +grub_x509_cert_parse (const void *data, grub_size_t size, grub_x509_cert_t *results); + +/* + * Release all the storage associated with the x509 certificate. If the caller + * dynamically allocated the certificate, it must free it. The caller is also + * responsible for maintenance of the linked list. + */ +extern void +grub_x509_cert_release (grub_x509_cert_t *cert); + /* * Parse a PKCS#7 message, which must be a signed data message. The message must * be in 'sigbuf' and of size 'data_size'. The result is placed in 'msg', which diff --git a/grub-core/commands/appendedsig/x509.c b/grub-core/commands/appendedsig/x509.c new file mode 100644 index 000000000..bc152663a --- /dev/null +++ b/grub-core/commands/appendedsig/x509.c @@ -0,0 +1,970 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020, 2022 Free Software Foundation, Inc. + * Copyright (C) 2020, 2022, 2025 IBM Corporation + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "appendedsig.h" + +static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + + +/* RFC 3279 2.3.1 RSA Keys. */ +static const char *rsaEncryption_oid = "1.2.840.113549.1.1.1"; + +/* RFC 5280 Appendix A. */ +static const char *commonName_oid = "2.5.4.3"; + +/* RFC 5280 4.2.1.3 Key Usage. */ +static const char *keyUsage_oid = "2.5.29.15"; + +static const grub_uint8_t digitalSignatureUsage = 0x80; + +/* RFC 5280 4.2.1.9 Basic Constraints. */ +static const char *basicConstraints_oid = "2.5.29.19"; + +/* RFC 5280 4.2.1.12 Extended Key Usage. */ +static const char *extendedKeyUsage_oid = "2.5.29.37"; +static const char *codeSigningUsage_oid = "1.3.6.1.5.5.7.3.3"; + +/* + * RFC 3279 2.3.1 + * + * The RSA public key MUST be encoded using the ASN.1 type RSAPublicKey: + * + * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER } -- e + * + * where modulus is the modulus n, and publicExponent is the public exponent e. + */ +static grub_err_t +grub_parse_rsa_pubkey (grub_uint8_t *der, grub_int32_t dersize, grub_x509_cert_t *certificate) +{ + grub_int32_t result; + asn1_node spk = NULL; + grub_uint8_t *m_data, *e_data; + grub_int32_t m_size, e_size; + grub_err_t err = GRUB_ERR_NONE; + gcry_error_t gcry_err; + + result = asn1_create_element (grub_gnutls_gnutls_asn, "GNUTLS.RSAPublicKey", &spk); + if (result != ASN1_SUCCESS) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "cannot create storage for public key ASN.1 data"); + + result = asn1_der_decoding2 (&spk, der, &dersize, ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, + "cannot decode certificate public key DER: %s", asn1_error); + goto cleanup; + } + + m_data = grub_asn1_allocate_and_read (spk, "modulus", "RSA modulus", &m_size); + if (m_data == NULL) + { + err = grub_errno; + goto cleanup; + } + + e_data = grub_asn1_allocate_and_read (spk, "publicExponent", "RSA public exponent", &e_size); + if (e_data == NULL) + { + err = grub_errno; + goto cleanup_m_data; + } + + /* + * Convert m, e to mpi + * + * nscanned is not set for FMT_USG, it's only set for FMT_PGP, so we can't + * verify it. + */ + gcry_err = _gcry_mpi_scan (&certificate->mpis[0], GCRYMPI_FMT_USG, m_data, m_size, NULL); + if (gcry_err != GPG_ERR_NO_ERROR) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, + "error loading RSA modulus into MPI structure: %d", gcry_err); + goto cleanup_e_data; + } + + gcry_err = _gcry_mpi_scan (&certificate->mpis[1], GCRYMPI_FMT_USG, e_data, e_size, NULL); + if (gcry_err != GPG_ERR_NO_ERROR) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, + "error loading RSA exponent into MPI structure: %d", gcry_err); + goto cleanup_m_mpi; + } + + /* RSA key size in bits. */ + certificate->modulus_size = (m_size * 8) - 8; + + grub_free (e_data); + grub_free (m_data); + asn1_delete_structure (&spk); + + return GRUB_ERR_NONE; + + cleanup_m_mpi: + _gcry_mpi_release (certificate->mpis[0]); + cleanup_e_data: + grub_free (e_data); + cleanup_m_data: + grub_free (m_data); + cleanup: + asn1_delete_structure (&spk); + + return err; +} + +/* + * RFC 5280: + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + * + * AlgorithmIdentifiers come from RFC 3279, we are not strictly compilant as we + * only support RSA Encryption. + */ +static grub_err_t +grub_x509_read_subject_public_key (asn1_node asn, grub_x509_cert_t *results) +{ + grub_int32_t result; + grub_err_t err; + const char *algo_name = "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm"; + const char *params_name = "tbsCertificate.subjectPublicKeyInfo.algorithm.parameters"; + const char *pk_name = "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey"; + char algo_oid[GRUB_MAX_OID_LEN]; + grub_int32_t algo_size = sizeof (algo_oid); + char params_value[2]; + grub_int32_t params_size = sizeof (params_value); + grub_uint8_t *key_data = NULL; + grub_int32_t key_size = 0; + grub_uint32_t key_type; + + /* Algorithm: see notes for rsaEncryption_oid. */ + result = asn1_read_value (asn, algo_name, algo_oid, &algo_size); + if (result != ASN1_SUCCESS) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading x509 public key algorithm: %s", + asn1_strerror (result)); + + if (grub_strncmp (algo_oid, rsaEncryption_oid, sizeof (rsaEncryption_oid)) != 0) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unsupported x509 public key algorithm: %s", algo_oid); + + /* + * RFC 3279 2.3.1 : The rsaEncryption OID is intended to be used in the + * algorithm field of a value of type AlgorithmIdentifier. The parameters + * field MUST have ASN.1 type NULL for this algorithm identifier. + */ + result = asn1_read_value (asn, params_name, params_value, ¶ms_size); + if (result != ASN1_SUCCESS) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading x509 public key parameters: %s", + asn1_strerror (result)); + + if (params_value[0] != ASN1_TAG_NULL) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "invalid x509 public key parameters: expected NULL"); + + /* + * RFC 3279 2.3.1: The DER encoded RSAPublicKey is the value of the BIT + * STRING subjectPublicKey. + */ + result = asn1_read_value_type (asn, pk_name, NULL, &key_size, &key_type); + if (result != ASN1_MEM_ERROR) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading size of x509 public key: %s", + asn1_strerror (result)); + if (key_type != ASN1_ETYPE_BIT_STRING) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "unexpected ASN.1 type when reading x509 public key: %x", + key_type); + + /* Length is in bits. */ + key_size = (key_size + 7) / 8; + + key_data = grub_malloc (key_size); + if (key_data == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory for x509 public key"); + + result = asn1_read_value (asn, pk_name, key_data, &key_size); + if (result != ASN1_SUCCESS) + { + grub_free (key_data); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading public key data"); + } + + key_size = (key_size + 7) / 8; + err = grub_parse_rsa_pubkey (key_data, key_size, results); + grub_free (key_data); + + return err; +} + +/* Decode a string as defined in Appendix A. */ +static grub_err_t +decode_string (char *der, grub_int32_t der_size, char **string, grub_size_t *string_size) +{ + asn1_node strasn; + grub_int32_t result; + char *choice; + grub_int32_t choice_size = 0; + grub_int32_t tmp_size = 0; + grub_err_t err = GRUB_ERR_NONE; + + result = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.DirectoryString", &strasn); + if (result != ASN1_SUCCESS) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not create ASN.1 structure for certificate: %s", + asn1_strerror (result)); + + result = asn1_der_decoding2 (&strasn, der, &der_size, ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, + "could not parse DER for DirectoryString: %s", asn1_error); + goto cleanup; + } + + choice = grub_asn1_allocate_and_read (strasn, "", "DirectoryString choice", &choice_size); + if (choice == NULL) + { + err = grub_errno; + goto cleanup; + } + + if (grub_strncmp ("utf8String", choice, choice_size) == 0) + { + result = asn1_read_value (strasn, "utf8String", NULL, &tmp_size); + if (result != ASN1_MEM_ERROR) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading size of UTF-8 string: %s", + asn1_strerror (result)); + goto cleanup_choice; + } + } + else if (grub_strncmp ("printableString", choice, choice_size) == 0) + { + result = asn1_read_value (strasn, "printableString", NULL, &tmp_size); + if (result != ASN1_MEM_ERROR) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading size of printableString: %s", + asn1_strerror (result)); + goto cleanup_choice; + } + } + else + { + err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "only UTF-8 and printable DirectoryStrings are supported, got %s", + choice); + goto cleanup_choice; + } + + /* Read size does not include trailing NUL. */ + tmp_size++; + + *string = grub_malloc (tmp_size); + if (*string == NULL) + { + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, + "cannot allocate memory for DirectoryString contents"); + goto cleanup_choice; + } + + result = asn1_read_value (strasn, choice, *string, &tmp_size); + if (result != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading out %s in DirectoryString: %s", + choice, asn1_strerror (result)); + grub_free (*string); + *string = NULL; + goto cleanup_choice; + } + + *string_size = tmp_size + 1; + (*string)[tmp_size] = '\0'; + + cleanup_choice: + grub_free (choice); + cleanup: + asn1_delete_structure (&strasn); + + return err; +} + +/* + * TBSCertificate ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * ... + * + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + */ +static grub_err_t +check_version (asn1_node certificate, grub_x509_cert_t *results) +{ + grub_int32_t rc; + const char *name = "tbsCertificate.version"; + grub_uint8_t version; + grub_int32_t len = sizeof (version); + + rc = asn1_read_value (certificate, name, &version, &len); + + /* Require version 3. */ + if (rc != ASN1_SUCCESS || len != 1) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading certificate version"); + + if (version != 0x02) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "invalid x509 certificate version, expected v3 (0x02), got 0x%02x.", + version); + + results->version = version; + + return GRUB_ERR_NONE; +} + +/* we extract only the CN and issuer. */ +static grub_err_t +read_name (asn1_node asn, const char *name_path, char **name, grub_size_t *name_size) +{ + grub_int32_t seq_components, set_components; + grub_int32_t result; + grub_int32_t i, j; + char *top_path, *set_path, *type_path, *val_path; + char type[GRUB_MAX_OID_LEN]; + grub_int32_t type_len = sizeof (type); + grub_int32_t string_size = 0; + char *string_der; + grub_err_t err; + + *name = NULL; + + top_path = grub_xasprintf ("%s.rdnSequence", name_path); + if (top_path == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not allocate memory for %s name parsing path", name_path); + + result = asn1_number_of_elements (asn, top_path, &seq_components); + if (result != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error counting name components: %s", + asn1_strerror (result)); + goto cleanup; + } + + for (i = 1; i <= seq_components; i++) + { + set_path = grub_xasprintf ("%s.?%d", top_path, i); + if (set_path == NULL) + { + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not allocate memory for %s name set parsing path", + name_path); + goto cleanup_set; + } + /* This brings us, hopefully, to a set. */ + result = asn1_number_of_elements (asn, set_path, &set_components); + if (result != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, + "error counting name sub-components components (element %d): %s", + i, asn1_strerror (result)); + goto cleanup_set; + } + for (j = 1; j <= set_components; j++) + { + type_path = grub_xasprintf ("%s.?%d.?%d.type", top_path, i, j); + if (type_path == NULL) + { + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not allocate memory for %s name component type path", + name_path); + goto cleanup_set; + } + type_len = sizeof (type); + result = asn1_read_value (asn, type_path, type, &type_len); + if (result != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading %s name component type: %s", + name_path, asn1_strerror (result)); + goto cleanup_type; + } + + if (grub_strncmp (type, commonName_oid, type_len) != 0) + { + grub_free (type_path); + continue; + } + + val_path = grub_xasprintf ("%s.?%d.?%d.value", top_path, i, j); + if (val_path == NULL) + { + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not allocate memory for %s name component value path", + name_path); + goto cleanup_type; + } + + string_der = grub_asn1_allocate_and_read (asn, val_path, name_path, &string_size); + if (string_der == NULL) + { + err = grub_errno; + goto cleanup_val_path; + } + + err = decode_string (string_der, string_size, name, name_size); + if (err) + goto cleanup_string; + + grub_free (string_der); + grub_free (type_path); + grub_free (val_path); + break; + } + + grub_free (set_path); + if (*name) + break; + } + + grub_free (top_path); + + return GRUB_ERR_NONE; + + cleanup_string: + grub_free (string_der); + cleanup_val_path: + grub_free (val_path); + cleanup_type: + grub_free (type_path); + cleanup_set: + grub_free (set_path); + cleanup: + grub_free (top_path); + + return err; +} + +/* Verify the Key Usage extension. We require the Digital Signature usage. */ +static grub_err_t +verify_key_usage (grub_uint8_t *value, grub_int32_t value_size) +{ + asn1_node usageasn; + grub_int32_t result; + grub_err_t err = GRUB_ERR_NONE; + grub_uint8_t usage = 0xff; + grub_int32_t usage_size = sizeof (usage_size); + + result = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.KeyUsage", &usageasn); + if (result != ASN1_SUCCESS) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not create ASN.1 structure for key usage"); + + result = asn1_der_decoding2 (&usageasn, value, &value_size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, + "error parsing DER for Key Usage: %s", asn1_error); + goto cleanup; + } + + result = asn1_read_value (usageasn, "", &usage, &usage_size); + if (result != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading Key Usage value: %s", + asn1_strerror (result)); + goto cleanup; + } + + if (!(usage & digitalSignatureUsage)) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, + "key usage (0x%x) missing Digital Signature usage", usage); + goto cleanup; + } + + cleanup: + asn1_delete_structure (&usageasn); + + return err; +} + +/* + * BasicConstraints ::= SEQUENCE { + * cA BOOLEAN DEFAULT FALSE, + * pathLenConstraint INTEGER (0..MAX) OPTIONAL } + */ +static grub_err_t +verify_basic_constraints (grub_uint8_t *value, grub_int32_t value_size) +{ + asn1_node basicasn; + grub_int32_t result; + grub_err_t err = GRUB_ERR_NONE; + char cA[6]; /* FALSE or TRUE. */ + grub_int32_t cA_size = sizeof (cA); + + result = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.BasicConstraints", &basicasn); + if (result != ASN1_SUCCESS) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not create ASN.1 structure for Basic Constraints"); + + result = asn1_der_decoding2 (&basicasn, value, &value_size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, + "error parsing DER for Basic Constraints: %s", asn1_error); + goto cleanup; + } + + result = asn1_read_value (basicasn, "cA", cA, &cA_size); + if (result == ASN1_ELEMENT_NOT_FOUND) + { + /* Not present, default is False, so this is OK. */ + err = GRUB_ERR_NONE; + goto cleanup; + } + else if (result != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading Basic Constraints cA value: %s", + asn1_strerror (result)); + goto cleanup; + } + + /* The certificate must not be a CA certificate. */ + if (grub_strncmp ("FALSE", cA, cA_size) != 0) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "unexpected CA value: %s", cA); + goto cleanup; + } + + cleanup: + asn1_delete_structure (&basicasn); + + return err; +} + +/* + * Verify the Extended Key Usage extension. We require the Code Signing usage. + * + * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + * + * KeyPurposeId ::= OBJECT IDENTIFIER + */ +static grub_err_t +verify_extended_key_usage (grub_uint8_t *value, grub_int32_t value_size) +{ + asn1_node extendedasn; + grub_int32_t result, count, i = 0; + grub_err_t err = GRUB_ERR_NONE; + char usage[GRUB_MAX_OID_LEN], name[3]; + grub_int32_t usage_size = sizeof (usage); + + result = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.ExtKeyUsageSyntax", &extendedasn); + if (result != ASN1_SUCCESS) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not create ASN.1 structure for Extended Key Usage"); + + result = asn1_der_decoding2 (&extendedasn, value, &value_size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, + "error parsing DER for Extended Key Usage: %s", asn1_error); + goto cleanup; + } + + /* If EKUs are present, it checks the presents of Code Signing usage. */ + result = asn1_number_of_elements (extendedasn, "", &count); + if (result != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error counting number of Extended Key Usages: %s", + asn1_strerror (result)); + goto cleanup; + } + + for (i = 1; i < count + 1; i++) + { + grub_memset (name, 0, sizeof (name)); + grub_snprintf (name, sizeof (name), "?%d", i); + result = asn1_read_value (extendedasn, name, usage, &usage_size); + if (result != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading Extended Key Usage: %s", + asn1_strerror (result)); + goto cleanup; + } + + if (grub_strncmp (codeSigningUsage_oid, usage, usage_size) == 0) + goto cleanup; + } + + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "extended key usage missing Code Signing usage"); + + cleanup: + asn1_delete_structure (&extendedasn); + + return err; +} + +/* + * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + * + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING + * -- contains the DER encoding of an ASN.1 value + * -- corresponding to the extension type identified + * -- by extnID + * } + * + * A certificate must: + * - contain the Digital Signature usage + * - not be a CA + * - contain no extended usages, or contain the Code Signing extended usage + * - not contain any other critical extensions (RFC 5280 s 4.2) + */ +static grub_err_t +verify_extensions (asn1_node cert) +{ + grub_int32_t result; + grub_int32_t ext, num_extensions = 0; + grub_int32_t usage_present = 0, constraints_present = 0, extended_usage_present = 0; + char *oid_path, *critical_path, *value_path; + char extnID[GRUB_MAX_OID_LEN]; + grub_int32_t extnID_size; + grub_err_t err; + char critical[6]; /* We get either "TRUE" or "FALSE". */ + grub_int32_t critical_size; + grub_uint8_t *value; + grub_int32_t value_size; + + result = asn1_number_of_elements (cert, "tbsCertificate.extensions", &num_extensions); + if (result != ASN1_SUCCESS) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error counting number of extensions: %s", + asn1_strerror (result)); + + if (num_extensions < 2) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "insufficient number of extensions for certificate, need at least 2, got %d", + num_extensions); + + for (ext = 1; ext <= num_extensions; ext++) + { + oid_path = grub_xasprintf ("tbsCertificate.extensions.?%d.extnID", ext); + if (oid_path == NULL) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error extension OID path is empty"); + return err; + } + + extnID_size = sizeof (extnID); + result = asn1_read_value (cert, oid_path, extnID, &extnID_size); + if (result != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading extension OID: %s", + asn1_strerror (result)); + goto cleanup_oid_path; + } + + critical_path = grub_xasprintf ("tbsCertificate.extensions.?%d.critical", ext); + if (critical_path == NULL) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error critical path is empty"); + goto cleanup_oid_path; + } + + critical_size = sizeof (critical); + result = asn1_read_value (cert, critical_path, critical, &critical_size); + if (result == ASN1_ELEMENT_NOT_FOUND) + critical[0] = '\0'; + else if (result != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading extension criticality: %s", + asn1_strerror (result)); + goto cleanup_critical_path; + } + + value_path = grub_xasprintf ("tbsCertificate.extensions.?%d.extnValue", ext); + if (value_path == NULL) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error extnValue path is empty"); + goto cleanup_critical_path; + } + + value = grub_asn1_allocate_and_read (cert, value_path, + "certificate extension value", &value_size); + if (value == NULL) + { + err = grub_errno; + goto cleanup_value_path; + } + + /* + * Now we must see if we recognise the OID. If we have an unrecognised + * critical extension we MUST bail. + */ + if (grub_strncmp (keyUsage_oid, extnID, extnID_size) == 0) + { + err = verify_key_usage (value, value_size); + if (err != GRUB_ERR_NONE) + goto cleanup_value; + + usage_present++; + } + else if (grub_strncmp (basicConstraints_oid, extnID, extnID_size) == 0) + { + err = verify_basic_constraints (value, value_size); + if (err != GRUB_ERR_NONE) + goto cleanup_value; + + constraints_present++; + } + else if (grub_strncmp (extendedKeyUsage_oid, extnID, extnID_size) == 0) + { + err = verify_extended_key_usage (value, value_size); + if (err != GRUB_ERR_NONE) + goto cleanup_value; + + extended_usage_present++; + } + else if (grub_strncmp ("TRUE", critical, critical_size) == 0) + { + /* + * Per the RFC, we must not process a certificate with a critical + * extension we do not understand. + */ + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, + "unhandled critical x509 extension with OID %s", extnID); + goto cleanup_value; + } + + grub_free (value); + grub_free (value_path); + grub_free (critical_path); + grub_free (oid_path); + } + + if (usage_present != 1) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "unexpected number of Key Usage extensions - expected 1, got %d", + usage_present); + + if (constraints_present != 1) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "unexpected number of basic constraints extensions - expected 1, got %d", + constraints_present); + + if (extended_usage_present > 1) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "unexpected number of Extended Key Usage extensions - expected 0 or 1, got %d", + extended_usage_present); + + return GRUB_ERR_NONE; + + cleanup_value: + grub_free (value); + cleanup_value_path: + grub_free (value_path); + cleanup_critical_path: + grub_free (critical_path); + cleanup_oid_path: + grub_free (oid_path); + + return err; +} + +static void +add_cert_fingerprint (const void *data, const grub_size_t data_size, + grub_x509_cert_t *const cert) +{ + /* Add SHA256 hash of certificate. */ + grub_crypto_hash ((gcry_md_spec_t *) &_gcry_digest_spec_sha256, + &cert->fingerprint[GRUB_FINGERPRINT_SHA256], data, data_size); + /* Add SHA384 hash of certificate. */ + grub_crypto_hash ((gcry_md_spec_t *) &_gcry_digest_spec_sha384, + &cert->fingerprint[GRUB_FINGERPRINT_SHA384], data, data_size); + /* Add SHA512 hash of certificate. */ + grub_crypto_hash ((gcry_md_spec_t *) &_gcry_digest_spec_sha512, + &cert->fingerprint[GRUB_FINGERPRINT_SHA512], data, data_size); +} + +/* + * Parse a certificate whose DER-encoded form is in @data, of size @data_size. + * Return the results in @results, which must point to an allocated x509 + * certificate. + */ +grub_err_t +grub_x509_cert_parse (const void *data, grub_size_t data_size, grub_x509_cert_t *results) +{ + grub_int32_t result = 0; + asn1_node cert; + grub_err_t err; + grub_int32_t tmp_size; + grub_int32_t size = (grub_int32_t) data_size; + + if (data_size > GRUB_UINT_MAX) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "cannot parse a certificate where data size > GRUB_UINT_MAX"); + + result = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.Certificate", &cert); + if (result != ASN1_SUCCESS) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not create ASN.1 structure for certificate: %s", + asn1_strerror (result)); + + result = asn1_der_decoding2 (&cert, data, &size, ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, + "could not parse DER for certificate: %s", asn1_error); + goto cleanup; + } + + /* + * TBSCertificate ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1 + */ + err = check_version (cert, results); + if (err != GRUB_ERR_NONE) + goto cleanup; + + /* + * serialNumber CertificateSerialNumber, + * + * CertificateSerialNumber ::= INTEGER + */ + results->serial = grub_asn1_allocate_and_read (cert, "tbsCertificate.serialNumber", + "certificate serial number", &tmp_size); + if (results->serial == NULL) + { + err = grub_errno; + goto cleanup; + } + /* + * It's safe to cast the signed int to an unsigned here, we know + * length is non-negative. + */ + results->serial_len = tmp_size; + + /* + * signature AlgorithmIdentifier, + * + * We don't load the signature or issuer at the moment, + * as we don't attempt x509 verification. + */ + + /* + * validity Validity, + * + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time } + * + * We can't validate this reasonably, we have no true time source on several + * platforms. For now we do not parse them. + */ + + /* + * issuer Name, + * + * This is an X501 name, we parse out just the issuer. + */ + err = read_name (cert, "tbsCertificate.issuer", &results->issuer, &results->issuer_len); + if (err != GRUB_ERR_NONE) + goto cleanup_serial; + + /* + * subject Name, + * + * This is an X501 name, we parse out just the CN. + */ + err = read_name (cert, "tbsCertificate.subject", &results->subject, &results->subject_len); + if (err != GRUB_ERR_NONE) + goto cleanup_issuer; + + /* + * TBSCertificate ::= SEQUENCE { + * ... + * subjectPublicKeyInfo SubjectPublicKeyInfo, + * ... + */ + err = grub_x509_read_subject_public_key (cert, results); + if (err != GRUB_ERR_NONE) + goto cleanup_name; + + /* + * TBSCertificate ::= SEQUENCE { + * ... + * extensions [3] EXPLICIT Extensions OPTIONAL + * -- If present, version MUST be v3 + * } + */ + err = verify_extensions (cert); + if (err != GRUB_ERR_NONE) + goto cleanup_mpis; + + /* + * We do not read or check the signature on the certificate: + * as discussed we do not try to validate the certificate but trust + * it implictly. + */ + asn1_delete_structure (&cert); + + /* Add the fingerprint of the certificate. */ + add_cert_fingerprint (data, data_size, results); + + return GRUB_ERR_NONE; + + cleanup_mpis: + _gcry_mpi_release (results->mpis[GRUB_RSA_PK_MODULUS]); + _gcry_mpi_release (results->mpis[GRUB_RSA_PK_EXPONENT]); + cleanup_name: + grub_free (results->subject); + cleanup_issuer: + grub_free (results->issuer); + cleanup_serial: + grub_free (results->serial); + cleanup: + asn1_delete_structure (&cert); + + return err; +} + +/* + * Release all the storage associated with the x509 certificate. If the caller + * dynamically allocated the certificate, it must free it. The caller is also + * responsible for maintenance of the linked list. + */ +void +grub_x509_cert_release (grub_x509_cert_t *cert) +{ + grub_free (cert->issuer); + grub_free (cert->subject); + grub_free (cert->serial); + _gcry_mpi_release (cert->mpis[GRUB_RSA_PK_MODULUS]); + _gcry_mpi_release (cert->mpis[GRUB_RSA_PK_EXPONENT]); +} diff --git a/include/grub/crypto.h b/include/grub/crypto.h index 6ab021cb8..e4e2bd1a7 100644 --- a/include/grub/crypto.h +++ b/include/grub/crypto.h @@ -518,6 +518,7 @@ grub_crypto_hmac_buffer (const struct gcry_md_spec *md, extern gcry_md_spec_t _gcry_digest_spec_md5; extern gcry_md_spec_t _gcry_digest_spec_sha1; extern gcry_md_spec_t _gcry_digest_spec_sha256; +extern gcry_md_spec_t _gcry_digest_spec_sha384; extern gcry_md_spec_t _gcry_digest_spec_sha512; extern gcry_md_spec_t _gcry_digest_spec_crc32; extern gcry_cipher_spec_t _gcry_cipher_spec_aes; From f8e8779d8e2bd30c990ed3551d0e170064ea1863 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:54:54 +0530 Subject: [PATCH 041/208] powerpc/ieee1275: Enter lockdown based on /ibm, secure-boot Read secure boot mode from 'ibm,secure-boot' property and if the secure boot mode is set to 2 (enforce), enter lockdown. Else it is considered as disabled. There are three secure boot modes. They are 0 - disabled No signature verification is performed. This is the default. 1 - audit Signature verification is performed and if signature verification fails, display the errors and allow the boot to continue. 2 - enforce Lockdown the GRUB. Signature verification is performed and if signature verification fails, display the errors and stop the boot. Now, only support disabled and enforce. Signed-off-by: Daniel Axtens Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Stefan Berger Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- docs/grub.texi | 2 +- grub-core/Makefile.core.def | 1 + grub-core/kern/ieee1275/init.c | 54 ++++++++++++++++++++++++++++++++++ include/grub/lockdown.h | 3 +- 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index 37297fc2c..1c33b44e2 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -9144,7 +9144,7 @@ platforms. @section Lockdown when booting on a secure setup The GRUB can be locked down when booted on a secure boot environment, for example -if the UEFI secure boot is enabled. On a locked down configuration, the GRUB will +if UEFI or Power secure boot is enabled. On a locked down configuration, the GRUB will be restricted and some operations/commands cannot be executed. This also includes limiting which filesystems are supported to those thought to be more robust and widely used within GRUB. diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 0fcf67f9d..8e3929710 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -333,6 +333,7 @@ kernel = { powerpc_ieee1275 = kern/powerpc/cache.S; powerpc_ieee1275 = kern/powerpc/dl.c; powerpc_ieee1275 = kern/powerpc/compiler-rt.S; + powerpc_ieee1275 = kern/lockdown.c; sparc64_ieee1275 = kern/sparc64/cache.S; sparc64_ieee1275 = kern/sparc64/dl.c; diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index a5586f85b..28653a5a4 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -49,6 +49,14 @@ #if defined(__powerpc__) || defined(__i386__) #include #endif +#if defined(__powerpc__) +#include +#endif + +#ifdef __powerpc__ +#define GRUB_SB_DISABLED ((grub_uint32_t) 0) +#define GRUB_SB_ENFORCE ((grub_uint32_t) 2) +#endif /* The maximum heap size we're going to claim at boot. Not used by sparc. */ #ifdef __i386__ @@ -994,7 +1002,49 @@ grub_parse_cmdline (void) } } } +#ifdef __powerpc__ +static void +grub_ieee1275_get_secure_boot (void) +{ + grub_ieee1275_phandle_t root; + grub_uint32_t sb_mode = GRUB_SB_DISABLED; + grub_int32_t rc; + rc = grub_ieee1275_finddevice ("/", &root); + if (rc != 0) + { + grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't find / node"); + return; + } + + rc = grub_ieee1275_get_integer_property (root, "ibm,secure-boot", &sb_mode, sizeof (sb_mode), 0); + if (rc != 0) + { + grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't examine /ibm,secure-boot property"); + return; + } + /* + * Secure Boot Mode: + * 0 - disabled + * No signature verification is performed. This is the default. + * 1 - audit + * Signature verification is performed and if signature verification + * fails, display the errors and allow the boot to continue. + * 2 - enforce + * Lockdown the GRUB. Signature verification is performed and If + * signature verification fails, display the errors and stop the boot. + * + * Now, only support disabled and enforce. + */ + if (sb_mode == GRUB_SB_ENFORCE) + { + grub_dprintf ("ieee1275", "Secure Boot Enabled\n"); + grub_lockdown (); + } + else + grub_dprintf ("ieee1275", "Secure Boot Disabled\n"); +} +#endif /* __powerpc__ */ grub_addr_t grub_modbase; void @@ -1020,6 +1070,10 @@ grub_machine_init (void) #else grub_install_get_time_ms (grub_rtc_get_time_ms); #endif + +#ifdef __powerpc__ + grub_ieee1275_get_secure_boot (); +#endif } void diff --git a/include/grub/lockdown.h b/include/grub/lockdown.h index 40531fa82..ebfee4bf0 100644 --- a/include/grub/lockdown.h +++ b/include/grub/lockdown.h @@ -24,7 +24,8 @@ #define GRUB_LOCKDOWN_DISABLED 0 #define GRUB_LOCKDOWN_ENABLED 1 -#ifdef GRUB_MACHINE_EFI +#if defined(GRUB_MACHINE_EFI) || \ + (defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275)) extern void EXPORT_FUNC (grub_lockdown) (void); extern int From 069f3614e6b4131bc20c7403f1413797ded6a15b Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:54:55 +0530 Subject: [PATCH 042/208] appended signatures: Support verifying appended signatures Building on the parsers and the ability to embed X.509 certificates, as well as the existing gcrypt functionality, add a module for verifying appended signatures. This includes a signature verifier that requires that the Linux kernel and GRUB modules have appended signatures for verification. Signature verification must be enabled by setting check_appended_signatures. If secure boot is enabled with enforce mode when the appendedsig module is loaded, signature verification will be enabled, and trusted keys will be extracted from the GRUB ELF Note and stored in the db and locked automatically. Signed-off-by: Daniel Axtens Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/Makefile.core.def | 15 + grub-core/commands/appendedsig/appendedsig.c | 581 +++++++++++++++++++ include/grub/err.h | 3 +- include/grub/file.h | 2 + 4 files changed, 600 insertions(+), 1 deletion(-) create mode 100644 grub-core/commands/appendedsig/appendedsig.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 8e3929710..6824a0ee4 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -997,6 +997,21 @@ module = { cppflags = '$(CPPFLAGS_GCRY)'; }; +module = { + name = appendedsig; + common = commands/appendedsig/appendedsig.c; + common = commands/appendedsig/x509.c; + common = commands/appendedsig/pkcs7.c; + common = commands/appendedsig/asn1util.c; + common = commands/appendedsig/gnutls_asn1_tab.c; + common = commands/appendedsig/pkix_asn1_tab.c; + enable = emu; + enable = powerpc_ieee1275; + cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls'; + cppflags = '$(CPPFLAGS_GCRY) -I$(srcdir)/lib/libtasn1-grub'; + depends = crypto, gcry_rsa, gcry_sha256, gcry_sha512, mpi, asn1; +}; + module = { name = hdparm; common = commands/hdparm.c; diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c new file mode 100644 index 000000000..e53efd2da --- /dev/null +++ b/grub-core/commands/appendedsig/appendedsig.c @@ -0,0 +1,581 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020, 2021, 2022 Free Software Foundation, Inc. + * Copyright (C) 2020, 2021, 2022, 2025 IBM Corporation + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "appendedsig.h" + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Public key type. */ +#define PKEY_ID_PKCS7 2 + +/* Appended signature magic string and size. */ +#define SIG_MAGIC "~Module signature appended~\n" +#define SIG_MAGIC_SIZE ((sizeof(SIG_MAGIC) - 1)) + +/* + * This structure is extracted from scripts/sign-file.c in the linux kernel + * source. It was licensed as LGPLv2.1+, which is GPLv3+ compatible. + */ +struct module_signature +{ + grub_uint8_t algo; /* Public-key crypto algorithm [0]. */ + grub_uint8_t hash; /* Digest algorithm [0]. */ + grub_uint8_t id_type; /* Key identifier type [PKEY_ID_PKCS7]. */ + grub_uint8_t signer_len; /* Length of signer's name [0]. */ + grub_uint8_t key_id_len; /* Length of key identifier [0]. */ + grub_uint8_t __pad[3]; + grub_uint32_t sig_len; /* Length of signature data. */ +} GRUB_PACKED; + +#define SIG_METADATA_SIZE (sizeof (struct module_signature)) +#define APPENDED_SIG_SIZE(pkcs7_data_size) \ + (pkcs7_data_size + SIG_MAGIC_SIZE + SIG_METADATA_SIZE) + +/* This represents an entire, parsed, appended signature. */ +struct appended_signature +{ + struct module_signature sig_metadata; /* Module signature metadata. */ + grub_pkcs7_data_t pkcs7; /* Parsed PKCS#7 data. */ + grub_size_t signature_len; /* Length of PKCS#7 data + metadata + magic. */ +}; +typedef struct appended_signature sb_appendedsig_t; + +/* This represents a trusted certificates. */ +struct sb_database +{ + grub_x509_cert_t *certs; /* Certificates. */ + grub_uint32_t cert_entries; /* Number of certificates. */ +}; +typedef struct sb_database sb_database_t; + +/* The db list is used to validate appended signatures. */ +static sb_database_t db = {.certs = NULL, .cert_entries = 0}; + +/* + * Signature verification flag (check_sigs). + * check_sigs: false + * - No signature verification. This is the default. + * check_sigs: true + * - Enforce signature verification, and if signature verification fails, post + * the errors and stop the boot. + */ +static bool check_sigs = false; + +static grub_ssize_t +pseudo_read (struct grub_file *file, char *buf, grub_size_t len) +{ + grub_memcpy (buf, (grub_uint8_t *) file->data + file->offset, len); + return len; +} + +/* Filesystem descriptor. */ +static struct grub_fs pseudo_fs = { + .name = "pseudo", + .fs_read = pseudo_read +}; + +static bool +is_cert_match (const grub_x509_cert_t *cert1, const grub_x509_cert_t *cert2) +{ + if (grub_memcmp (cert1->subject, cert2->subject, cert2->subject_len) == 0 + && grub_memcmp (cert1->issuer, cert2->issuer, cert2->issuer_len) == 0 + && grub_memcmp (cert1->serial, cert2->serial, cert2->serial_len) == 0 + && grub_memcmp (cert1->mpis[GRUB_RSA_PK_MODULUS], cert2->mpis[GRUB_RSA_PK_MODULUS], + sizeof (cert2->mpis[GRUB_RSA_PK_MODULUS])) == 0 + && grub_memcmp (cert1->mpis[GRUB_RSA_PK_EXPONENT], cert2->mpis[GRUB_RSA_PK_EXPONENT], + sizeof (cert2->mpis[GRUB_RSA_PK_EXPONENT])) == 0 + && grub_memcmp (cert1->fingerprint[GRUB_FINGERPRINT_SHA256], + cert2->fingerprint[GRUB_FINGERPRINT_SHA256], + grub_strlen ((char *) cert2->fingerprint[GRUB_FINGERPRINT_SHA256])) == 0) + return true; + + return false; +} + +/* Check the certificate presence in the db list. */ +static bool +check_cert_presence (const grub_x509_cert_t *cert_in, const sb_database_t *sb_database) +{ + grub_x509_cert_t *cert; + + for (cert = sb_database->certs; cert != NULL; cert = cert->next) + if (is_cert_match (cert, cert_in) == true) + return true; + + return false; +} + +/* Add the certificate into the db list */ +static grub_err_t +add_certificate (const grub_uint8_t *data, const grub_size_t data_size, + sb_database_t *sb_database) +{ + grub_err_t rc; + grub_x509_cert_t *cert; + + if (data == NULL || data_size == 0) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "certificate data or size is not available"); + + cert = grub_zalloc (sizeof (grub_x509_cert_t)); + if (cert == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + + rc = grub_x509_cert_parse (data, data_size, cert); + if (rc != GRUB_ERR_NONE) + { + grub_dprintf ("appendedsig", "cannot add a certificate CN='%s' to the db list\n", + cert->subject); + grub_free (cert); + return rc; + } + + if (check_cert_presence (cert, sb_database) == true) + { + grub_dprintf ("appendedsig", + "cannot add a certificate CN='%s', as it is present in the db list", + cert->subject); + grub_x509_cert_release (cert); + grub_free (cert); + + return GRUB_ERR_EXISTS; + } + + grub_dprintf ("appendedsig", "added a certificate CN='%s' to the db list\n", + cert->subject); + + cert->next = sb_database->certs; + sb_database->certs = cert; + sb_database->cert_entries++; + + return rc; +} + +static grub_err_t +file_read_whole (grub_file_t file, grub_uint8_t **buf, grub_size_t *len) +{ + grub_off_t full_file_size; + grub_size_t file_size, total_read_size = 0; + grub_ssize_t read_size; + + full_file_size = grub_file_size (file); + if (full_file_size == GRUB_FILE_SIZE_UNKNOWN) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "cannot read a file of unknown size into a buffer"); + + if (full_file_size > GRUB_SIZE_MAX) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "file is too large to read: %" PRIuGRUB_OFFSET " bytes", + full_file_size); + + file_size = (grub_size_t) full_file_size; + *buf = grub_malloc (file_size); + if (*buf == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not allocate file data buffer size %" PRIuGRUB_SIZE, + file_size); + + while (total_read_size < file_size) + { + read_size = grub_file_read (file, *buf + total_read_size, file_size - total_read_size); + if (read_size < 0) + { + grub_free (*buf); + return grub_errno; + } + else if (read_size == 0) + { + grub_free (*buf); + return grub_error (GRUB_ERR_IO, + "could not read full file size " + "(%" PRIuGRUB_SIZE "), only %" PRIuGRUB_SIZE " bytes read", + file_size, total_read_size); + } + + total_read_size += read_size; + } + + *len = file_size; + + return GRUB_ERR_NONE; +} + +static grub_err_t +extract_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize, + sb_appendedsig_t *sig) +{ + grub_size_t appendedsig_pkcs7_size; + grub_size_t signed_data_size = bufsize; + const grub_uint8_t *signed_data = buf; + + if (signed_data_size < SIG_MAGIC_SIZE) + return grub_error (GRUB_ERR_BAD_SIGNATURE, "file too short for signature magic"); + + /* Fast-forwarding pointer and get signature magic string. */ + signed_data += signed_data_size - SIG_MAGIC_SIZE; + if (grub_strncmp ((const char *) signed_data, SIG_MAGIC, SIG_MAGIC_SIZE)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, "missing or invalid signature magic"); + + signed_data_size -= SIG_MAGIC_SIZE; + if (signed_data_size < SIG_METADATA_SIZE) + return grub_error (GRUB_ERR_BAD_SIGNATURE, "file too short for signature metadata"); + + /* Rewind pointer and extract signature metadata. */ + signed_data -= SIG_METADATA_SIZE; + grub_memcpy (&(sig->sig_metadata), signed_data, SIG_METADATA_SIZE); + + if (sig->sig_metadata.id_type != PKEY_ID_PKCS7) + return grub_error (GRUB_ERR_BAD_SIGNATURE, "wrong signature type"); + + appendedsig_pkcs7_size = grub_be_to_cpu32 (sig->sig_metadata.sig_len); + + signed_data_size -= SIG_METADATA_SIZE; + if (appendedsig_pkcs7_size > signed_data_size) + return grub_error (GRUB_ERR_BAD_SIGNATURE, "file too short for PKCS#7 message"); + + grub_dprintf ("appendedsig", "sig len %" PRIuGRUB_SIZE "\n", appendedsig_pkcs7_size); + + /* Appended signature size. */ + sig->signature_len = APPENDED_SIG_SIZE (appendedsig_pkcs7_size); + /* Rewind pointer and parse appended pkcs7 data. */ + signed_data -= appendedsig_pkcs7_size; + + return grub_pkcs7_data_parse (signed_data, appendedsig_pkcs7_size, &sig->pkcs7); +} + +/* + * Given a hash value 'hval', of hash specification 'hash', prepare the + * S-expressions (sexp) and perform the signature verification. + */ +static grub_err_t +verify_signature (const gcry_mpi_t *pkmpi, const gcry_mpi_t hmpi, + const gcry_md_spec_t *hash, const grub_uint8_t *hval) +{ + gcry_sexp_t hsexp, pubkey, sig; + grub_size_t errof; + + if (_gcry_sexp_build (&hsexp, &errof, "(data (flags %s) (hash %s %b))", "pkcs1", + hash->name, hash->mdlen, hval) != GPG_ERR_NO_ERROR) + return GRUB_ERR_BAD_SIGNATURE; + + if (_gcry_sexp_build (&pubkey, &errof, "(public-key (dsa (n %M) (e %M)))", + pkmpi[0], pkmpi[1]) != GPG_ERR_NO_ERROR) + return GRUB_ERR_BAD_SIGNATURE; + + if (_gcry_sexp_build (&sig, &errof, "(sig-val (rsa (s %M)))", hmpi) != GPG_ERR_NO_ERROR) + return GRUB_ERR_BAD_SIGNATURE; + + _gcry_sexp_dump (sig); + _gcry_sexp_dump (hsexp); + _gcry_sexp_dump (pubkey); + + if (grub_crypto_pk_rsa->verify (sig, hsexp, pubkey) != GPG_ERR_NO_ERROR) + return GRUB_ERR_BAD_SIGNATURE; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize) +{ + grub_err_t err; + grub_size_t datasize; + void *context; + grub_uint8_t *hash; + grub_x509_cert_t *pk; + sb_appendedsig_t sig; + grub_pkcs7_signer_t *si; + grub_int32_t i; + + if (!db.cert_entries) + return grub_error (GRUB_ERR_BAD_SIGNATURE, "no trusted keys to verify against"); + + err = extract_appended_signature (buf, bufsize, &sig); + if (err != GRUB_ERR_NONE) + return err; + + datasize = bufsize - sig.signature_len; + + /* Verify signature using trusted keys from db list. */ + for (i = 0; i < sig.pkcs7.signer_count; i++) + { + si = &sig.pkcs7.signers[i]; + context = grub_zalloc (si->hash->contextsize); + if (context == NULL) + return grub_errno; + + si->hash->init (context, 0); + si->hash->write (context, buf, datasize); + si->hash->final (context); + hash = si->hash->read (context); + + grub_dprintf ("appendedsig", "data size %" PRIuGRUB_SIZE ", signer %d hash %02x%02x%02x%02x...\n", + datasize, i, hash[0], hash[1], hash[2], hash[3]); + + for (pk = db.certs; pk != NULL; pk = pk->next) + { + err = verify_signature (pk->mpis, si->sig_mpi, si->hash, hash); + if (err == GRUB_ERR_NONE) + { + grub_dprintf ("appendedsig", "verify signer %d with key '%s' succeeded\n", + i, pk->subject); + break; + } + + grub_dprintf ("appendedsig", "verify signer %d with key '%s' failed\n", + i, pk->subject); + } + + grub_free (context); + if (err == GRUB_ERR_NONE) + break; + } + + grub_pkcs7_data_release (&sig.pkcs7); + + if (err != GRUB_ERR_NONE) + return grub_error (err, "failed to verify signature against a trusted key"); + + return err; +} + +/* + * Extract the X.509 certificates from the ELF Note header, parse it, and add + * it to the db list. + */ +static void +load_elf2db (void) +{ + grub_err_t err; + struct grub_module_header *header; + struct grub_file pseudo_file; + grub_uint8_t *cert_data = NULL; + grub_size_t cert_data_size = 0; + + FOR_MODULES (header) + { + /* Not an X.509 certificate, skip. */ + if (header->type != OBJ_TYPE_X509_PUBKEY) + continue; + + grub_memset (&pseudo_file, 0, sizeof (pseudo_file)); + pseudo_file.fs = &pseudo_fs; + pseudo_file.size = header->size - sizeof (struct grub_module_header); + pseudo_file.data = (char *) header + sizeof (struct grub_module_header); + + grub_dprintf ("appendedsig", "found an X.509 certificate, size=%" PRIuGRUB_UINT64_T "\n", + pseudo_file.size); + + err = file_read_whole (&pseudo_file, &cert_data, &cert_data_size); + if (err == GRUB_ERR_OUT_OF_MEMORY) + return; + else if (err != GRUB_ERR_NONE) + continue; + + err = add_certificate (cert_data, cert_data_size, &db); + grub_free (cert_data); + if (err == GRUB_ERR_OUT_OF_MEMORY) + return; + } +} + +/* Free db list memory */ +static void +free_db_list (void) +{ + grub_x509_cert_t *cert; + + while (db.certs != NULL) + { + cert = db.certs; + db.certs = db.certs->next; + grub_x509_cert_release (cert); + grub_free (cert); + } + + grub_memset (&db, 0, sizeof (sb_database_t)); +} + +static const char * +grub_env_read_sec (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + if (check_sigs == true) + return "yes"; + + return "no"; +} + +static char * +grub_env_write_sec (struct grub_env_var *var __attribute__ ((unused)), const char *val) +{ + char *ret; + + /* + * Do not allow the value to be changed if signature verification is enabled + * (check_sigs is set to true) and GRUB is locked down. + */ + if (check_sigs == true && grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED) + { + ret = grub_strdup ("yes"); + if (ret == NULL) + grub_error (GRUB_ERR_OUT_OF_MEMORY, "could not duplicate a string enforce"); + + return ret; + } + + if (grub_strcmp (val, "yes") == 0) + check_sigs = true; + else if (grub_strcmp (val, "no") == 0) + check_sigs = false; + + ret = grub_strdup (grub_env_read_sec (NULL, NULL)); + if (ret == NULL) + grub_error (GRUB_ERR_OUT_OF_MEMORY, "could not duplicate a string %s", + grub_env_read_sec (NULL, NULL)); + + return ret; +} + +static grub_err_t +appendedsig_init (grub_file_t io __attribute__ ((unused)), enum grub_file_type type, + void **context __attribute__ ((unused)), enum grub_verify_flags *flags) +{ + if (check_sigs == false) + { + *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + return GRUB_ERR_NONE; + } + + switch (type & GRUB_FILE_TYPE_MASK) + { + case GRUB_FILE_TYPE_CERTIFICATE_TRUST: + /* + * This is a certificate to add to trusted keychain. + * + * This needs to be verified or blocked. Ideally we'd write an x509 + * verifier, but we lack the hubris required to take this on. Instead, + * require that it have an appended signature. + */ + case GRUB_FILE_TYPE_LINUX_KERNEL: + case GRUB_FILE_TYPE_GRUB_MODULE: + /* + * Appended signatures are only defined for ELF binaries. Out of an + * abundance of caution, we only verify Linux kernels and GRUB modules + * at this point. + */ + *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK; + return GRUB_ERR_NONE; + + case GRUB_FILE_TYPE_ACPI_TABLE: + case GRUB_FILE_TYPE_DEVICE_TREE_IMAGE: + /* + * It is possible to use appended signature verification without + * lockdown - like the PGP verifier. When combined with an embedded + * config file in a signed GRUB binary, this could still be a meaningful + * secure-boot chain - so long as it isn't subverted by something like a + * rouge ACPI table or DT image. Defer them explicitly. + */ + *flags = GRUB_VERIFY_FLAGS_DEFER_AUTH; + return GRUB_ERR_NONE; + + default: + *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + return GRUB_ERR_NONE; + } +} + +static grub_err_t +appendedsig_write (void *ctxt __attribute__ ((unused)), void *buf, grub_size_t size) +{ + return grub_verify_appended_signature (buf, size); +} + +struct grub_file_verifier grub_appendedsig_verifier = { + .name = "appendedsig", + .init = appendedsig_init, + .write = appendedsig_write, +}; + +GRUB_MOD_INIT (appendedsig) +{ + grub_int32_t rc; + + /* + * If secure boot is enabled with enforce mode and GRUB is locked down, enable + * signature verification. + */ + if (grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED) + check_sigs = true; + + /* + * This is appended signature verification environment variable. It is + * automatically set to either "no" or "yes" based on the ’ibm,secure-boot’ + * device tree property. + * + * "no": No signature verification. This is the default. + * + * "yes": Enforce signature verification. When GRUB is locked down, user cannot + * change the value by setting the check_appended_signatures variable + * back to ‘no’ + */ + grub_register_variable_hook ("check_appended_signatures", grub_env_read_sec, grub_env_write_sec); + grub_env_export ("check_appended_signatures"); + + rc = grub_asn1_init (); + if (rc != ASN1_SUCCESS) + grub_fatal ("error initing ASN.1 data structures: %d: %s\n", rc, asn1_strerror (rc)); + + /* Extract trusted keys from ELF Note and store them in the db. */ + load_elf2db (); + grub_dprintf ("appendedsig", "the db list now has %u static keys\n", + db.cert_entries); + + grub_verifier_register (&grub_appendedsig_verifier); + grub_dl_set_persistent (mod); +} + +GRUB_MOD_FINI (appendedsig) +{ + /* + * grub_dl_set_persistent should prevent this from actually running, but it + * does still run under emu. + */ + + free_db_list (); + grub_register_variable_hook ("check_appended_signatures", NULL, NULL); + grub_env_unset ("check_appended_signatures"); + grub_verifier_unregister (&grub_appendedsig_verifier); +} diff --git a/include/grub/err.h b/include/grub/err.h index 202fa8a7a..6ab905c93 100644 --- a/include/grub/err.h +++ b/include/grub/err.h @@ -75,7 +75,8 @@ typedef enum GRUB_ERR_BAD_SIGNATURE, GRUB_ERR_BAD_FIRMWARE, GRUB_ERR_STILL_REFERENCED, - GRUB_ERR_RECURSION_DEPTH + GRUB_ERR_RECURSION_DEPTH, + GRUB_ERR_EXISTS } grub_err_t; diff --git a/include/grub/file.h b/include/grub/file.h index a5bf3a792..d678de063 100644 --- a/include/grub/file.h +++ b/include/grub/file.h @@ -80,6 +80,8 @@ enum grub_file_type GRUB_FILE_TYPE_PUBLIC_KEY, /* File holding public key to add to trused keys. */ GRUB_FILE_TYPE_PUBLIC_KEY_TRUST, + /* File holding x509 certificiate to add to trusted keys. */ + GRUB_FILE_TYPE_CERTIFICATE_TRUST, /* File of which we intend to print a blocklist to the user. */ GRUB_FILE_TYPE_PRINT_BLOCKLIST, /* File we intend to use for test loading or testing speed. */ From 76158ed1ad8f3b6278830abee237bc3f9e3e46fa Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:54:56 +0530 Subject: [PATCH 043/208] powerpc/ieee1275: Read the db and dbx secure boot variables Enhancing the infrastructure to enable the Platform Keystore (PKS) feature, which provides access to the SB_VERSION, db, and dbx secure boot variables from PKS. If PKS is enabled, it will read secure boot variables such as db and dbx from PKS and extract EFI Signature List (ESL) from it. The ESLs would be saved in the Platform Keystore buffer, and the appendedsig module would read it later to extract the certificate's details from ESL. In the following scenarios, static key management mode will be activated: 1. When Secure Boot is enabled with static key management mode 2. When SB_VERSION is unavailable but Secure Boot is enabled 3. When PKS support is unavailable but Secure Boot is enabled Note: SB_VERSION: Key Management Mode 1 - Enable dynamic key management mode. Read the db and dbx variables from PKS, and use them for signature verification. 0 - Enable static key management mode. Read keys from the GRUB ELF Note and use it for signature verification. Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/Makefile.am | 2 + grub-core/Makefile.core.def | 2 + grub-core/kern/ieee1275/ieee1275.c | 1 - grub-core/kern/ieee1275/init.c | 4 + grub-core/kern/powerpc/ieee1275/ieee1275.c | 137 +++++++ .../kern/powerpc/ieee1275/platform_keystore.c | 333 ++++++++++++++++++ include/grub/ieee1275/ieee1275.h | 3 + include/grub/powerpc/ieee1275/ieee1275.h | 36 ++ .../grub/powerpc/ieee1275/platform_keystore.h | 123 +++++++ 9 files changed, 640 insertions(+), 1 deletion(-) create mode 100644 grub-core/kern/powerpc/ieee1275/ieee1275.c create mode 100644 grub-core/kern/powerpc/ieee1275/platform_keystore.c create mode 100644 include/grub/powerpc/ieee1275/platform_keystore.h diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index e50db8106..8577462d5 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -246,6 +246,8 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/alloc.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/powerpc/ieee1275/ieee1275.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/powerpc/ieee1275/platform_keystore.h endif if COND_sparc64_ieee1275 diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 6824a0ee4..853d879a4 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -334,6 +334,8 @@ kernel = { powerpc_ieee1275 = kern/powerpc/dl.c; powerpc_ieee1275 = kern/powerpc/compiler-rt.S; powerpc_ieee1275 = kern/lockdown.c; + powerpc_ieee1275 = kern/powerpc/ieee1275/ieee1275.c; + powerpc_ieee1275 = kern/powerpc/ieee1275/platform_keystore.c; sparc64_ieee1275 = kern/sparc64/cache.S; sparc64_ieee1275 = kern/sparc64/dl.c; diff --git a/grub-core/kern/ieee1275/ieee1275.c b/grub-core/kern/ieee1275/ieee1275.c index 36ca2dbfc..afa37a9f0 100644 --- a/grub-core/kern/ieee1275/ieee1275.c +++ b/grub-core/kern/ieee1275/ieee1275.c @@ -23,7 +23,6 @@ #define IEEE1275_PHANDLE_INVALID ((grub_ieee1275_cell_t) -1) #define IEEE1275_IHANDLE_INVALID ((grub_ieee1275_cell_t) 0) -#define IEEE1275_CELL_INVALID ((grub_ieee1275_cell_t) -1) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 28653a5a4..0ef888058 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -51,6 +51,8 @@ #endif #if defined(__powerpc__) #include +#include +#include #endif #ifdef __powerpc__ @@ -1043,6 +1045,8 @@ grub_ieee1275_get_secure_boot (void) } else grub_dprintf ("ieee1275", "Secure Boot Disabled\n"); + + grub_pks_keystore_init (); } #endif /* __powerpc__ */ grub_addr_t grub_modbase; diff --git a/grub-core/kern/powerpc/ieee1275/ieee1275.c b/grub-core/kern/powerpc/ieee1275/ieee1275.c new file mode 100644 index 000000000..20c49e371 --- /dev/null +++ b/grub-core/kern/powerpc/ieee1275/ieee1275.c @@ -0,0 +1,137 @@ +/* ieee1275.c - Access the Open Firmware client interface. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc. + * Copyright (C) 2020, 2021, 2022, 2023, 2024, 2025 IBM Corporation + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ +#include +#include +#include + +grub_int32_t +grub_ieee1275_test (const char *interface_name) +{ + struct test_args + { + struct grub_ieee1275_common_hdr common;/* The header information like interface name, number of inputs and outputs. */ + grub_ieee1275_cell_t name; /* The interface name. */ + grub_ieee1275_cell_t missing; + } args; + + INIT_IEEE1275_COMMON (&args.common, "test", 1, 1); + args.name = (grub_ieee1275_cell_t) interface_name; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + + if (args.missing == IEEE1275_CELL_INVALID) + return -1; + + return 0; +} + +grub_int32_t +grub_ieee1275_pks_max_object_size (grub_uint32_t *result) +{ + struct mos_args + { + struct grub_ieee1275_common_hdr common;/* The header information like interface name, number of inputs and outputs. */ + grub_ieee1275_cell_t size; /* The maximum object size for a PKS object. */ + } args; + + INIT_IEEE1275_COMMON (&args.common, GRUB_PKS_MAX_OBJ_INTERFACE, 0, 1); + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + + if (args.size == IEEE1275_CELL_INVALID || args.size == 0) + return -1; + + *result = args.size; + + return 0; +} + +grub_int32_t +grub_ieee1275_pks_read_object (const grub_uint32_t consumer, const char *label, + const grub_uint32_t label_len, const grub_uint32_t buffer_len, + grub_uint8_t *buffer, grub_uint32_t *data_len, + grub_uint32_t *policies) +{ + struct pks_read_args + { + struct grub_ieee1275_common_hdr common; /* The header information like interface name, number of inputs and outputs. */ + grub_ieee1275_cell_t consumer; /* The object belonging to consumer with the label. */ + grub_ieee1275_cell_t label; /* Object label buffer logical real address. */ + grub_ieee1275_cell_t label_len; /* The byte length of the object label. */ + grub_ieee1275_cell_t buffer; /* Output buffer logical real address. */ + grub_ieee1275_cell_t buffer_len; /* Length of the output buffer. */ + grub_ieee1275_cell_t data_len; /* The number of bytes copied to the output buffer. */ + grub_ieee1275_cell_t policies; /* The object policies. */ + grub_int32_t rc; /* The return code. */ + } args; + + INIT_IEEE1275_COMMON (&args.common, GRUB_PKS_READ_OBJ_INTERFACE, 5, 3); + args.consumer = consumer; + args.label_len = label_len; + args.buffer_len = buffer_len; + args.label = (grub_ieee1275_cell_t) label; + args.buffer = (grub_ieee1275_cell_t) buffer; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + + if (args.data_len == IEEE1275_CELL_INVALID) + return -1; + + *data_len = args.data_len; + *policies = args.policies; + + return args.rc; +} + +grub_int32_t +grub_ieee1275_pks_read_sbvar (const grub_uint32_t sbvar_flags, const grub_uint32_t sbvar_type, + const grub_uint32_t buffer_len, grub_uint8_t *buffer, + grub_size_t *data_len) +{ + struct pks_read_sbvar_args + { + struct grub_ieee1275_common_hdr common; /* The header information like interface name, number of inputs and outputs. */ + grub_ieee1275_cell_t sbvar_flags; /* The sbvar operation flags. */ + grub_ieee1275_cell_t sbvar_type; /* The sbvar being requested. */ + grub_ieee1275_cell_t buffer; /* Output buffer logical real address. */ + grub_ieee1275_cell_t buffer_len; /* Length of the Output buffer. */ + grub_ieee1275_cell_t data_len; /* The number of bytes copied to the output buffer. */ + grub_int32_t rc; /* The return code. */ + } args; + + INIT_IEEE1275_COMMON (&args.common, GRUB_PKS_READ_SBVAR_INTERFACE, 4, 2); + args.sbvar_flags = sbvar_flags; + args.sbvar_type = sbvar_type; + args.buffer_len = buffer_len; + args.buffer = (grub_ieee1275_cell_t) buffer; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + + if (args.data_len == IEEE1275_CELL_INVALID) + return -1; + + *data_len = args.data_len; + + return args.rc; +} diff --git a/grub-core/kern/powerpc/ieee1275/platform_keystore.c b/grub-core/kern/powerpc/ieee1275/platform_keystore.c new file mode 100644 index 000000000..cc2d493bb --- /dev/null +++ b/grub-core/kern/powerpc/ieee1275/platform_keystore.c @@ -0,0 +1,333 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2024 Free Software Foundation, Inc. + * Copyright (C) 2022, 2023, 2024, 2025 IBM Corporation + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +/* PKS object maximum size. */ +static grub_uint32_t pks_max_object_size = 0; + +/* Platform KeyStore db and dbx. */ +static grub_pks_t pks_keystore = { .db = NULL, .dbx = NULL, .db_entries = 0, + .dbx_entries = 0, .db_exists = true}; +/* + * pks_use_keystore: Key Management Modes + * False: Static key management (use built-in Keys). This is default. + * True: Dynamic key management (use Platform KeySotre). + */ +static bool pks_use_keystore = false; + +/* + * Reads the Globally Unique Identifier (GUID), EFI Signature Database (ESD), + * and its size from the Platform KeyStore EFI Signature List (ESL), then + * stores them into the PKS Signature Database (SD) (i.e., pks_sd buffer + * and pks_sd entries) in the GRUB. + */ +static grub_err_t +_esl_to_esd (const grub_uint8_t *esl_data, grub_size_t esl_size, + const grub_size_t signature_size, const grub_packed_guid_t *guid, + grub_pks_sd_t **pks_sd, grub_uint32_t *pks_sd_entries) +{ + grub_esd_t *esd; + grub_pks_sd_t *signature = *pks_sd; + grub_uint32_t entries = *pks_sd_entries; + grub_size_t data_size, offset = 0; + + /* Reads the ESD from ESL. */ + while (esl_size > 0) + { + esd = (grub_esd_t *) (esl_data + offset); + data_size = signature_size - sizeof (grub_esd_t); + + signature = grub_realloc (signature, (entries + 1) * sizeof (grub_pks_sd_t)); + if (signature == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + + signature[entries].data = grub_malloc (data_size * sizeof (grub_uint8_t)); + if (signature[entries].data == NULL) + { + /* Allocated memory will be freed by grub_pks_free_data(). */ + *pks_sd = signature; + *pks_sd_entries = entries + 1; + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + } + + grub_memcpy (signature[entries].data, esd->signature_data, data_size); + signature[entries].data_size = data_size; + signature[entries].guid = *guid; + entries++; + esl_size -= signature_size; + offset += signature_size; + } + + *pks_sd = signature; + *pks_sd_entries = entries; + + return GRUB_ERR_NONE; +} + +/* Extract the ESD after removing the ESL header from ESL. */ +static grub_err_t +esl_to_esd (const grub_uint8_t *esl_data, grub_size_t *next_esl, + grub_pks_sd_t **pks_sd, grub_uint32_t *pks_sd_entries) +{ + grub_packed_guid_t guid; + grub_esl_t *esl; + grub_size_t offset, esl_size, signature_size, signature_header_size; + + /* Convert the ESL data into the ESL. */ + esl = (grub_esl_t *) esl_data; + if (*next_esl < sizeof (grub_esl_t) || esl == NULL) + return grub_error (GRUB_ERR_BUG, "invalid ESL"); + + esl_size = grub_le_to_cpu32 (esl->signature_list_size); + signature_header_size = grub_le_to_cpu32 (esl->signature_header_size); + signature_size = grub_le_to_cpu32 (esl->signature_size); + grub_memcpy (&guid, &esl->signature_type, sizeof (grub_packed_guid_t)); + + if (esl_size < sizeof (grub_esl_t) || esl_size > *next_esl) + return grub_error (GRUB_ERR_BUG, "invalid ESL size (%u)\n", esl_size); + + *next_esl = esl_size; + offset = sizeof (grub_esl_t) + signature_header_size; + esl_size = esl_size - offset; + + return _esl_to_esd (esl_data + offset, esl_size, signature_size, &guid, + pks_sd, pks_sd_entries); +} + +/* + * Import the EFI Signature Database (ESD) and the number of ESD from the ESL + * into the pks_sd buffer and pks_sd entries. + */ +static grub_err_t +pks_sd_from_esl (const grub_uint8_t *esl_data, grub_size_t esl_size, + grub_pks_sd_t **pks_sd, grub_uint32_t *pks_sd_entries) +{ + grub_err_t rc; + grub_size_t next_esl = esl_size; + + do + { + rc = esl_to_esd (esl_data, &next_esl, pks_sd, pks_sd_entries); + if (rc != GRUB_ERR_NONE) + break; + + esl_data += next_esl; + esl_size -= next_esl; + next_esl = esl_size; + } + while (esl_size > 0); + + return rc; +} + +/* Read the secure boot version from PKS as an object. Caller must free result. */ +static grub_err_t +read_sbversion_from_pks (grub_uint8_t **out) +{ + grub_int32_t rc; + grub_uint32_t outlen = 0, policy = 0; + + *out = grub_malloc (pks_max_object_size); + if (*out == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + + rc = grub_ieee1275_pks_read_object (GRUB_PKS_CONSUMER_FW, GRUB_SB_VERSION_KEY_NAME, + GRUB_SB_VERSION_KEY_LEN, pks_max_object_size, *out, + &outlen, &policy); + if (rc < 0) + { + grub_free (*out); + return grub_error (GRUB_ERR_READ_ERROR, "SB version read failed (%d)\n", rc); + } + + if (outlen != 1 || (**out >= 2)) + { + grub_free (*out); + return grub_error (GRUB_ERR_BAD_NUMBER, "found unexpected SB version: %u\n", **out); + } + + return GRUB_ERR_NONE; +} + +/* + * Reads the secure boot variable from PKS, unpacks it, read the ESD from ESL, + * and store the information in the pks_sd buffer. + */ +static grub_err_t +read_sbvar_from_pks (const grub_uint32_t sbvarflags, const grub_uint32_t sbvartype, + grub_pks_sd_t **pks_sd, grub_uint32_t *pks_sd_entries) +{ + grub_int32_t rc; + grub_err_t err = GRUB_ERR_NONE; + grub_uint8_t *esl_data = NULL; + grub_size_t esl_data_size = 0; + + esl_data = grub_malloc (pks_max_object_size); + if (esl_data == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + + rc = grub_ieee1275_pks_read_sbvar (sbvarflags, sbvartype, pks_max_object_size, + esl_data, &esl_data_size); + if (rc == IEEE1275_CELL_NOT_FOUND) + { + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, "secure boot variable %s not found (%d)", + (sbvartype == GRUB_PKS_SBVAR_DB) ? "db" : "dbx", rc); + goto fail; + } + else if (rc < 0) + { + err = grub_error (GRUB_ERR_READ_ERROR, "secure boot variable %s reading (%d)", + (sbvartype == GRUB_PKS_SBVAR_DB) ? "db" : "dbx", rc); + goto fail; + } + + if (esl_data_size > 0) + err = pks_sd_from_esl (esl_data, esl_data_size, pks_sd, pks_sd_entries); + else + err = GRUB_ERR_BAD_NUMBER; + + fail: + grub_free (esl_data); + + return err; +} + +/* + * Test the availability of PKS support. If PKS support is avaialble and objects + * present, it reads the secure boot version (SB_VERSION) from PKS. + * + * SB_VERSION: Key Management Mode + * 1 - Enable dynamic key management mode. Read the db and dbx variables from PKS, + * and use them for signature verification. + * 0 - Enable static key management mode. Read keys from the GRUB ELF Note and use + * it for signature verification. + */ +static bool +is_pks_present (void) +{ + grub_err_t err; + grub_int32_t rc; + grub_uint8_t *data = NULL; + bool ret = false; + + rc = grub_ieee1275_test (GRUB_PKS_MAX_OBJ_INTERFACE); + if (rc < 0) + { + grub_error (GRUB_ERR_BAD_FIRMWARE, "firmware doesn't have PKS support\n"); + return ret; + } + else + { + rc = grub_ieee1275_pks_max_object_size (&pks_max_object_size); + if (rc < 0) + { + grub_error (GRUB_ERR_BAD_NUMBER, "PKS support is there but it has zero objects\n"); + return ret; + } + } + + err = read_sbversion_from_pks (&data); + if (err != GRUB_ERR_NONE) + return ret; + + /* + * If *data == 1, use dynamic key management and read the keys from the PKS. + * Else, use static key management and read the keys from the GRUB ELF Note. + */ + ret = ((*data == 1) ? true : false); + + grub_free (data); + + return ret; +} + +/* Free allocated memory. */ +void +grub_pks_free_data (void) +{ + grub_size_t i; + + for (i = 0; i < pks_keystore.db_entries; i++) + grub_free (pks_keystore.db[i].data); + + for (i = 0; i < pks_keystore.dbx_entries; i++) + grub_free (pks_keystore.dbx[i].data); + + grub_free (pks_keystore.db); + grub_free (pks_keystore.dbx); + grub_memset (&pks_keystore, 0, sizeof (grub_pks_t)); +} + +grub_pks_t * +grub_pks_get_keystore (void) +{ + return (pks_use_keystore == true) ? &pks_keystore : NULL; +} + +/* Initialization of the Platform KeyStore. */ +void +grub_pks_keystore_init (void) +{ + grub_err_t rc_db, rc_dbx; + + grub_dprintf ("ieee1275", "trying to load Platform KeyStore\n"); + + if (is_pks_present () == false) + { + grub_dprintf ("ieee1275", "Platform PKS is not available\n"); + return; + } + + /* + * When read db from PKS, there are three scenarios + * 1. db fully loaded from PKS + * 2. db partially loaded from PKS + * 3. no keys are loaded from db (if db does not exist in PKS), default to + * built-in keys (static keys) + * each of these scenarios, the db keys are checked against dbx. + */ + rc_db = read_sbvar_from_pks (0, GRUB_PKS_SBVAR_DB, &pks_keystore.db, &pks_keystore.db_entries); + if (rc_db == GRUB_ERR_FILE_NOT_FOUND) + pks_keystore.db_exists = false; + + /* + * Read dbx from PKS. If dbx is not completely loaded from PKS, then this + * could lead to the loading of vulnerable GRUB modules and kernel binaries. + * So, this should be prevented by freeing up loaded dbx and db. + */ + rc_dbx = read_sbvar_from_pks (0, GRUB_PKS_SBVAR_DBX, &pks_keystore.dbx, &pks_keystore.dbx_entries); + if (rc_dbx == GRUB_ERR_FILE_NOT_FOUND || rc_dbx == GRUB_ERR_BAD_NUMBER) + rc_dbx = GRUB_ERR_NONE; + + if (rc_dbx != GRUB_ERR_NONE) + grub_pks_free_data (); + + /* + * At this point, it's evident that PKS infrastructure exists, so the PKS + * keystore must be used for validating appended signatures. + */ + pks_use_keystore = true; +} diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h index c445d0499..157ed57be 100644 --- a/include/grub/ieee1275/ieee1275.h +++ b/include/grub/ieee1275/ieee1275.h @@ -24,6 +24,9 @@ #include #include +#define IEEE1275_CELL_INVALID ((grub_ieee1275_cell_t) -1) +#define IEEE1275_CELL_NOT_FOUND ((grub_int32_t) -7) + #define GRUB_IEEE1275_CELL_FALSE ((grub_ieee1275_cell_t) 0) #define GRUB_IEEE1275_CELL_TRUE ((grub_ieee1275_cell_t) -1) diff --git a/include/grub/powerpc/ieee1275/ieee1275.h b/include/grub/powerpc/ieee1275/ieee1275.h index 4eb207018..4b9966dd4 100644 --- a/include/grub/powerpc/ieee1275/ieee1275.h +++ b/include/grub/powerpc/ieee1275/ieee1275.h @@ -28,4 +28,40 @@ typedef grub_uint32_t grub_ieee1275_cell_t; #define PRIxGRUB_IEEE1275_CELL_T PRIxGRUB_UINT32_T #define PRIuGRUB_IEEE1275_CELL_T PRIuGRUB_UINT32_T +#ifdef __powerpc__ +/* The maximum object size interface name for a PKS object. */ +#define GRUB_PKS_MAX_OBJ_INTERFACE "pks-max-object-size" + +/* PKS read object and read sbvar interface name. */ +#define GRUB_PKS_READ_OBJ_INTERFACE "pks-read-object" +#define GRUB_PKS_READ_SBVAR_INTERFACE "pks-read-sbvar" + +/* PKS read object label for secure boot version. */ +#define GRUB_SB_VERSION_KEY_NAME "SB_VERSION" +#define GRUB_SB_VERSION_KEY_LEN (sizeof (GRUB_SB_VERSION_KEY_NAME) - 1) + +/* PKS consumer type for firmware. */ +#define GRUB_PKS_CONSUMER_FW ((grub_uint32_t) 1) + +/* PKS read secure boot variable request type for db and dbx. */ +#define GRUB_PKS_SBVAR_DB ((grub_uint32_t) 1) +#define GRUB_PKS_SBVAR_DBX ((grub_uint32_t) 2) + +extern grub_int32_t +grub_ieee1275_test (const char *interface_name); + +extern grub_int32_t +grub_ieee1275_pks_max_object_size (grub_uint32_t *result); + +extern grub_int32_t +grub_ieee1275_pks_read_object (const grub_uint32_t consumer, const char *label, + const grub_uint32_t label_len, const grub_uint32_t buffer_len, + grub_uint8_t *buffer, grub_uint32_t *data_len, + grub_uint32_t *policies); + +extern grub_int32_t +grub_ieee1275_pks_read_sbvar (const grub_uint32_t sbvar_flags, const grub_uint32_t sbvar_type, + const grub_uint32_t buffer_len, grub_uint8_t *buffer, + grub_size_t *data_len); +#endif /* __powerpc__ */ #endif /* ! GRUB_IEEE1275_MACHINE_HEADER */ diff --git a/include/grub/powerpc/ieee1275/platform_keystore.h b/include/grub/powerpc/ieee1275/platform_keystore.h new file mode 100644 index 000000000..931ada224 --- /dev/null +++ b/include/grub/powerpc/ieee1275/platform_keystore.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved. This + * program and the accompanying materials are licensed and made available + * under the terms and conditions of the 2-Clause BSD License which + * accompanies this distribution. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * https://github.com/tianocore/edk2-staging (edk2-staging repo of tianocore), + * the ImageAuthentication.h file under it, and here's the copyright and license. + * + * MdePkg/Include/Guid/ImageAuthentication.h + * + * Copyright 2022, 2023, 2024, 2025 IBM Corp. + */ + +#ifndef PLATFORM_KEYSTORE_HEADER +#define PLATFORM_KEYSTORE_HEADER 1 + +#include +#include +#include + +/* + * It is derived from EFI_SIGNATURE_DATA + * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h + * + * The structure of an EFI Signature Database (ESD). */ +struct grub_esd +{ + /* + * An identifier which identifies the agent which added the signature to + * the list. + */ + grub_packed_guid_t signature_owner; + /* The format of the signature is defined by the SignatureType. */ + grub_uint8_t signature_data[]; +} GRUB_PACKED; +typedef struct grub_esd grub_esd_t; + +/* + * It is derived from EFI_SIGNATURE_LIST + * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h + * + * The structure of an EFI Signature List (ESL). */ +struct grub_esl +{ + /* Type of the signature. GUID signature types are defined in below. */ + grub_packed_guid_t signature_type; + /* Total size of the signature list, including this header. */ + grub_uint32_t signature_list_size; + /* Size of the signature header which precedes the array of signatures. */ + grub_uint32_t signature_header_size; + /* Size of each signature.*/ + grub_uint32_t signature_size; +} GRUB_PACKED; +typedef struct grub_esl grub_esl_t; + +/* The structure of a PKS Signature Database (SD). */ +struct grub_pks_sd +{ + grub_packed_guid_t guid; /* Signature type. */ + grub_uint8_t *data; /* Signature data. */ + grub_size_t data_size; /* Size of signature data. */ +} GRUB_PACKED; +typedef struct grub_pks_sd grub_pks_sd_t; + +/* The structure of a Platform KeyStore (PKS). */ +struct grub_pks +{ + grub_pks_sd_t *db; /* Signature database. */ + grub_pks_sd_t *dbx; /* Forbidden signature database. */ + grub_uint32_t db_entries; /* Size of signature database. */ + grub_uint32_t dbx_entries;/* Size of forbidden signature database. */ + bool db_exists; /* Flag to indicate if the db exists or not in PKS. */ +}; +typedef struct grub_pks grub_pks_t; + +#if defined(__powerpc__) +/* Initialization of the Platform Keystore. */ +extern void +grub_pks_keystore_init (void); + +/* Platform KeyStore db and dbx. */ +extern grub_pks_t * +EXPORT_FUNC (grub_pks_get_keystore) (void); + +/* Free allocated memory. */ +extern void +EXPORT_FUNC (grub_pks_free_data) (void); +#else +static inline grub_pks_t * +grub_pks_get_keystore (void) +{ + return NULL; +} + +static inline void +grub_pks_free_data (void) +{ +} +#endif /* __powerpc__ */ +#endif From b5e872417d620f11e918977d5b63275774a28bce Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:54:57 +0530 Subject: [PATCH 044/208] appended signatures: Introducing key management environment variable Introducing the appended signature key management environment variable. It is automatically set to either "static" or "dynamic" based on the Platform KeyStore. "static": Enforce static key management signature verification. This is the default. When the GRUB is locked down, user cannot change the value by setting the appendedsig_key_mgmt variable back to "dynamic". "dynamic": Enforce dynamic key management signature verification. When the GRUB is locked down, user cannot change the value by setting the appendedsig_key_mgmt variable back to "static". Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/commands/appendedsig/appendedsig.c | 75 ++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c index e53efd2da..ca54c90fa 100644 --- a/grub-core/commands/appendedsig/appendedsig.c +++ b/grub-core/commands/appendedsig/appendedsig.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "appendedsig.h" @@ -94,6 +95,16 @@ static sb_database_t db = {.certs = NULL, .cert_entries = 0}; */ static bool check_sigs = false; +/* + * append_key_mgmt: Key Management Modes + * False: Static key management (use built-in Keys). This is default. + * True: Dynamic key management (use Platform KeySotre). + */ +static bool append_key_mgmt = false; + +/* Platform KeyStore db and dbx. */ +static grub_pks_t *pks_keystore; + static grub_ssize_t pseudo_read (struct grub_file *file, char *buf, grub_size_t len) { @@ -469,6 +480,46 @@ grub_env_write_sec (struct grub_env_var *var __attribute__ ((unused)), const cha return ret; } +static const char * +grub_env_read_key_mgmt (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + if (append_key_mgmt == true) + return "dynamic"; + + return "static"; +} + +static char * +grub_env_write_key_mgmt (struct grub_env_var *var __attribute__ ((unused)), const char *val) +{ + char *ret; + + /* + * Do not allow the value to be changed if signature verification is enabled + * (check_sigs is set to true) and GRUB is locked down. + */ + if (check_sigs == true && grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED) + { + ret = grub_strdup (grub_env_read_key_mgmt (NULL, NULL)); + if (ret == NULL) + grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + + return ret; + } + + if (grub_strcmp (val, "dynamic") == 0) + append_key_mgmt = true; + else if (grub_strcmp (val, "static") == 0) + append_key_mgmt = false; + + ret = grub_strdup (grub_env_read_key_mgmt (NULL, NULL)); + if (ret == NULL) + grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + + return ret; +} + static grub_err_t appendedsig_init (grub_file_t io __attribute__ ((unused)), enum grub_file_type type, void **context __attribute__ ((unused)), enum grub_verify_flags *flags) @@ -540,6 +591,11 @@ GRUB_MOD_INIT (appendedsig) if (grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED) check_sigs = true; + /* If PKS keystore is available, use dynamic key management. */ + pks_keystore = grub_pks_get_keystore (); + if (pks_keystore != NULL) + append_key_mgmt = true; + /* * This is appended signature verification environment variable. It is * automatically set to either "no" or "yes" based on the ’ibm,secure-boot’ @@ -554,6 +610,23 @@ GRUB_MOD_INIT (appendedsig) grub_register_variable_hook ("check_appended_signatures", grub_env_read_sec, grub_env_write_sec); grub_env_export ("check_appended_signatures"); + /* + * This is appended signature key management environment variable. It is + * automatically set to either "static" or "dynamic" based on the + * Platform KeyStore. + * + * "static": Enforce static key management signature verification. This is + * the default. When the GRUB is locked down, user cannot change + * the value by setting the appendedsig_key_mgmt variable back to + * "dynamic". + * + * "dynamic": Enforce dynamic key management signature verification. When the + * GRUB is locked down, user cannot change the value by setting the + * appendedsig_key_mgmt variable back to "static". + */ + grub_register_variable_hook ("appendedsig_key_mgmt", grub_env_read_key_mgmt, grub_env_write_key_mgmt); + grub_env_export ("appendedsig_key_mgmt"); + rc = grub_asn1_init (); if (rc != ASN1_SUCCESS) grub_fatal ("error initing ASN.1 data structures: %d: %s\n", rc, asn1_strerror (rc)); @@ -577,5 +650,7 @@ GRUB_MOD_FINI (appendedsig) free_db_list (); grub_register_variable_hook ("check_appended_signatures", NULL, NULL); grub_env_unset ("check_appended_signatures"); + grub_register_variable_hook ("appendedsig_key_mgmt", NULL, NULL); + grub_env_unset ("appendedsig_key_mgmt"); grub_verifier_unregister (&grub_appendedsig_verifier); } From 97f7001e19ed29fbb39b583550f17886434f1965 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:54:58 +0530 Subject: [PATCH 045/208] appended signatures: Create db and dbx lists If secure boot is enabled with static key management mode, the trusted certificates will be extracted from the GRUB ELF Note and added to db list. If secure boot is enabled with dynamic key management mode, the trusted certificates and certificate/binary hash will be extracted from the PKS and added to db list. The distrusted certificates, certificate/binary hash are read from the PKS and added to dbx list. Both dbx and db lists usage is added by a subsequent patch. Note: - If db does not exist in the PKS storage, then read the static keys as a db default keys from the GRUB ELF Note and add them into the db list. - If the certificate or the certificate hash exists in the dbx list, then do not add that certificate/certificate hash to the db list. Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/commands/appendedsig/appendedsig.c | 407 ++++++++++++++++++- include/grub/efi/pks.h | 112 +++++ include/grub/types.h | 4 + 3 files changed, 506 insertions(+), 17 deletions(-) create mode 100644 include/grub/efi/pks.h diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c index ca54c90fa..0c4c788ff 100644 --- a/grub-core/commands/appendedsig/appendedsig.c +++ b/grub-core/commands/appendedsig/appendedsig.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "appendedsig.h" @@ -46,6 +47,11 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define SIG_MAGIC "~Module signature appended~\n" #define SIG_MAGIC_SIZE ((sizeof(SIG_MAGIC) - 1)) +/* SHA256, SHA384 and SHA512 hash sizes. */ +#define SHA256_HASH_SIZE 32 +#define SHA384_HASH_SIZE 48 +#define SHA512_HASH_SIZE 64 + /* * This structure is extracted from scripts/sign-file.c in the linux kernel * source. It was licensed as LGPLv2.1+, which is GPLv3+ compatible. @@ -79,11 +85,23 @@ struct sb_database { grub_x509_cert_t *certs; /* Certificates. */ grub_uint32_t cert_entries; /* Number of certificates. */ + grub_uint8_t **hashes; /* Certificate/binary hashes. */ + grub_size_t *hash_sizes; /* Sizes of certificate/binary hashes. */ + grub_uint32_t hash_entries; /* Number of certificate/binary hashes. */ + bool is_db; /* Flag to indicate the db/dbx list. */ }; typedef struct sb_database sb_database_t; /* The db list is used to validate appended signatures. */ -static sb_database_t db = {.certs = NULL, .cert_entries = 0}; +static sb_database_t db = {.certs = NULL, .cert_entries = 0, .hashes = NULL, + .hash_sizes = NULL, .hash_entries = 0, .is_db = true}; +/* + * The dbx list is used to ensure that the distrusted certificates or GRUB + * modules/kernel binaries are rejected during appended signatures/hashes + * validation. + */ +static sb_database_t dbx = {.certs = NULL, .cert_entries = 0, .hashes = NULL, + .hash_sizes = NULL, .hash_entries = 0, .is_db = false}; /* * Signature verification flag (check_sigs). @@ -118,6 +136,169 @@ static struct grub_fs pseudo_fs = { .fs_read = pseudo_read }; +/* + * GUID can be used to determine the hashing function and generate the hash using + * determined hashing function. + */ +static grub_err_t +get_hash (const grub_packed_guid_t *guid, const grub_uint8_t *data, const grub_size_t data_size, + grub_uint8_t *hash, grub_size_t *hash_size) +{ + gcry_md_spec_t *hash_func = NULL; + + if (guid == NULL) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "GUID is not available"); + + if (grub_memcmp (guid, &GRUB_PKS_CERT_SHA256_GUID, GRUB_PACKED_GUID_SIZE) == 0 || + grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA256_GUID, GRUB_PACKED_GUID_SIZE) == 0) + hash_func = &_gcry_digest_spec_sha256; + else if (grub_memcmp (guid, &GRUB_PKS_CERT_SHA384_GUID, GRUB_PACKED_GUID_SIZE) == 0 || + grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA384_GUID, GRUB_PACKED_GUID_SIZE) == 0) + hash_func = &_gcry_digest_spec_sha384; + else if (grub_memcmp (guid, &GRUB_PKS_CERT_SHA512_GUID, GRUB_PACKED_GUID_SIZE) == 0 || + grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA512_GUID, GRUB_PACKED_GUID_SIZE) == 0) + hash_func = &_gcry_digest_spec_sha512; + else + return grub_error (GRUB_ERR_OUT_OF_RANGE, "unsupported GUID hash"); + + grub_crypto_hash (hash_func, hash, data, data_size); + *hash_size = hash_func->mdlen; + + return GRUB_ERR_NONE; +} + +static grub_err_t +generate_cert_hash (const grub_size_t cert_hash_size, const grub_uint8_t *data, + const grub_size_t data_size, grub_uint8_t *hash, grub_size_t *hash_size) +{ + grub_packed_guid_t guid = { 0 }; + + /* support SHA256, SHA384 and SHA512 for certificate hash */ + if (cert_hash_size == SHA256_HASH_SIZE) + grub_memcpy (&guid, &GRUB_PKS_CERT_X509_SHA256_GUID, GRUB_PACKED_GUID_SIZE); + else if (cert_hash_size == SHA384_HASH_SIZE) + grub_memcpy (&guid, &GRUB_PKS_CERT_X509_SHA384_GUID, GRUB_PACKED_GUID_SIZE); + else if (cert_hash_size == SHA512_HASH_SIZE) + grub_memcpy (&guid, &GRUB_PKS_CERT_X509_SHA512_GUID, GRUB_PACKED_GUID_SIZE); + else + { + grub_dprintf ("appendedsig", "unsupported hash type (%" PRIuGRUB_SIZE ") and " + "skipped\n", cert_hash_size); + return GRUB_ERR_UNKNOWN_COMMAND; + } + + return get_hash (&guid, data, data_size, hash, hash_size); +} + +/* Check the hash presence in the db/dbx list. */ +static bool +check_hash_presence (grub_uint8_t *const hash, const grub_size_t hash_size, + const sb_database_t *sb_database) +{ + grub_uint32_t i; + + for (i = 0; i < sb_database->hash_entries; i++) + { + if (sb_database->hashes[i] == NULL) + continue; + + if (hash_size == sb_database->hash_sizes[i] && + grub_memcmp (sb_database->hashes[i], hash, hash_size) == 0) + return true; + } + + return false; +} + +/* Add the certificate/binary hash into the db/dbx list. */ +static grub_err_t +add_hash (grub_uint8_t *const data, const grub_size_t data_size, sb_database_t *sb_database) +{ + grub_uint8_t **hashes; + grub_size_t *hash_sizes; + + if (data == NULL || data_size == 0) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "certificate/binary-hash data or size is not available"); + + if (sb_database->is_db == true) + { + if (check_hash_presence (data, data_size, &dbx) == true) + { + grub_dprintf ("appendedsig", + "cannot add a hash (%02x%02x%02x%02x), as it is present in the dbx list\n", + data[0], data[1], data[2], data[3]); + return GRUB_ERR_ACCESS_DENIED; + } + } + + if (check_hash_presence (data, data_size, sb_database) == true) + { + grub_dprintf ("appendedsig", + "cannot add a hash (%02x%02x%02x%02x), as it is present in the %s list\n", + data[0], data[1], data[2], data[3], ((sb_database->is_db == true) ? "db" : "dbx")); + return GRUB_ERR_EXISTS; + } + + hashes = grub_realloc (sb_database->hashes, sizeof (grub_uint8_t *) * (sb_database->hash_entries + 1)); + if (hashes == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + + hash_sizes = grub_realloc (sb_database->hash_sizes, sizeof (grub_size_t) * (sb_database->hash_entries + 1)); + if (hash_sizes == NULL) + { + /* Allocated memory will be freed by free_db_list()/free_dbx_list(). */ + hashes[sb_database->hash_entries] = NULL; + sb_database->hashes = hashes; + sb_database->hash_entries++; + + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + } + + hashes[sb_database->hash_entries] = grub_malloc (data_size); + if (hashes[sb_database->hash_entries] == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + + grub_dprintf ("appendedsig", + "added the hash %02x%02x%02x%02x... with size of %" PRIuGRUB_SIZE " to the %s list\n", + data[0], data[1], data[2], data[3], data_size, + ((sb_database->is_db == true) ? "db" : "dbx")); + + grub_memcpy (hashes[sb_database->hash_entries], data, data_size); + hash_sizes[sb_database->hash_entries] = data_size; + sb_database->hash_sizes = hash_sizes; + sb_database->hashes = hashes; + sb_database->hash_entries++; + + return GRUB_ERR_NONE; +} + +static bool +is_hash (const grub_packed_guid_t *guid) +{ + /* GUID type of the binary hash. */ + if (grub_memcmp (guid, &GRUB_PKS_CERT_SHA256_GUID, GRUB_PACKED_GUID_SIZE) == 0 || + grub_memcmp (guid, &GRUB_PKS_CERT_SHA384_GUID, GRUB_PACKED_GUID_SIZE) == 0 || + grub_memcmp (guid, &GRUB_PKS_CERT_SHA512_GUID, GRUB_PACKED_GUID_SIZE) == 0) + return true; + + /* GUID type of the certificate hash. */ + if (grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA256_GUID, GRUB_PACKED_GUID_SIZE) == 0 || + grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA384_GUID, GRUB_PACKED_GUID_SIZE) == 0 || + grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA512_GUID, GRUB_PACKED_GUID_SIZE) == 0) + return true; + + return false; +} + +static bool +is_x509 (const grub_packed_guid_t *guid) +{ + if (grub_memcmp (guid, &GRUB_PKS_CERT_X509_GUID, GRUB_PACKED_GUID_SIZE) == 0) + return true; + + return false; +} + static bool is_cert_match (const grub_x509_cert_t *cert1, const grub_x509_cert_t *cert2) { @@ -136,7 +317,33 @@ is_cert_match (const grub_x509_cert_t *cert1, const grub_x509_cert_t *cert2) return false; } -/* Check the certificate presence in the db list. */ +/* Check the certificate hash presence in the dbx list. */ +static bool +is_cert_hash_present_in_dbx (const grub_uint8_t *data, const grub_size_t data_size) +{ + grub_err_t rc; + grub_uint32_t i; + grub_size_t cert_hash_size = 0; + grub_uint8_t cert_hash[GRUB_MAX_HASH_LEN] = { 0 }; + + for (i = 0; i < dbx.hash_entries; i++) + { + if (dbx.hashes[i] == NULL) + continue; + + rc = generate_cert_hash (dbx.hash_sizes[i], data, data_size, cert_hash, &cert_hash_size); + if (rc != GRUB_ERR_NONE) + continue; + + if (cert_hash_size == dbx.hash_sizes[i] && + grub_memcmp (dbx.hashes[i], cert_hash, cert_hash_size) == 0) + return true; + } + + return false; +} + +/* Check the certificate presence in the db/dbx list. */ static bool check_cert_presence (const grub_x509_cert_t *cert_in, const sb_database_t *sb_database) { @@ -149,7 +356,11 @@ check_cert_presence (const grub_x509_cert_t *cert_in, const sb_database_t *sb_da return false; } -/* Add the certificate into the db list */ +/* + * Add the certificate into the db list if it is not present in the dbx and db + * list when is_db is true. Add the certificate into the dbx list when is_db is + * false. + */ static grub_err_t add_certificate (const grub_uint8_t *data, const grub_size_t data_size, sb_database_t *sb_database) @@ -167,30 +378,54 @@ add_certificate (const grub_uint8_t *data, const grub_size_t data_size, rc = grub_x509_cert_parse (data, data_size, cert); if (rc != GRUB_ERR_NONE) { - grub_dprintf ("appendedsig", "cannot add a certificate CN='%s' to the db list\n", - cert->subject); + grub_dprintf ("appendedsig", "cannot add a certificate CN='%s' to the %s list\n", + cert->subject, (sb_database->is_db == true) ? "db" : "dbx"); grub_free (cert); return rc; } + /* + * Only checks the certificate against dbx if is_db is true when dynamic key + * management is enabled. + */ + if (append_key_mgmt == true) + { + if (sb_database->is_db == true) + { + if (is_cert_hash_present_in_dbx (data, data_size) == true || + check_cert_presence (cert, &dbx) == true) + { + grub_dprintf ("appendedsig", + "cannot add a certificate CN='%s', as it is present in the dbx list", + cert->subject); + rc = GRUB_ERR_ACCESS_DENIED; + goto fail; + } + } + } + if (check_cert_presence (cert, sb_database) == true) { grub_dprintf ("appendedsig", - "cannot add a certificate CN='%s', as it is present in the db list", - cert->subject); - grub_x509_cert_release (cert); - grub_free (cert); - - return GRUB_ERR_EXISTS; + "cannot add a certificate CN='%s', as it is present in the %s list", + cert->subject, ((sb_database->is_db == true) ? "db" : "dbx")); + rc = GRUB_ERR_EXISTS; + goto fail; } - grub_dprintf ("appendedsig", "added a certificate CN='%s' to the db list\n", - cert->subject); + grub_dprintf ("appendedsig", "added a certificate CN='%s' to the %s list\n", + cert->subject, ((sb_database->is_db == true) ? "db" : "dbx")); cert->next = sb_database->certs; sb_database->certs = cert; sb_database->cert_entries++; + return rc; + + fail: + grub_x509_cert_release (cert); + grub_free (cert); + return rc; } @@ -382,6 +617,68 @@ grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize) return err; } +/* Add the X.509 certificates/binary hash to the db list from PKS. */ +static grub_err_t +load_pks2db (void) +{ + grub_err_t rc; + grub_uint32_t i; + + for (i = 0; i < pks_keystore->db_entries; i++) + { + if (is_hash (&pks_keystore->db[i].guid) == true) + { + rc = add_hash (pks_keystore->db[i].data, + pks_keystore->db[i].data_size, &db); + if (rc == GRUB_ERR_OUT_OF_MEMORY) + return rc; + } + else if (is_x509 (&pks_keystore->db[i].guid) == true) + { + rc = add_certificate (pks_keystore->db[i].data, + pks_keystore->db[i].data_size, &db); + if (rc == GRUB_ERR_OUT_OF_MEMORY) + return rc; + } + else + grub_dprintf ("appendedsig", "unsupported signature data type and " + "skipped (%u)\n", i + 1); + } + + return GRUB_ERR_NONE; +} + +/* Add the certificates and certificate/binary hash to the dbx list from PKS. */ +static grub_err_t +load_pks2dbx (void) +{ + grub_err_t rc; + grub_uint32_t i; + + for (i = 0; i < pks_keystore->dbx_entries; i++) + { + if (is_x509 (&pks_keystore->dbx[i].guid) == true) + { + rc = add_certificate (pks_keystore->dbx[i].data, + pks_keystore->dbx[i].data_size, &dbx); + if (rc == GRUB_ERR_OUT_OF_MEMORY) + return rc; + } + else if (is_hash (&pks_keystore->dbx[i].guid) == true) + { + rc = add_hash (pks_keystore->dbx[i].data, + pks_keystore->dbx[i].data_size, &dbx); + if (rc != GRUB_ERR_NONE) + return rc; + } + else + grub_dprintf ("appendedsig", "unsupported signature data type and " + "skipped (%u)\n", i + 1); + } + + return GRUB_ERR_NONE; +} + /* * Extract the X.509 certificates from the ELF Note header, parse it, and add * it to the db list. @@ -422,11 +719,45 @@ load_elf2db (void) } } +/* + * Extract trusted and distrusted keys from PKS and store them in the db and + * dbx list. + */ +static void +create_dbs_from_pks (void) +{ + grub_err_t err; + + err = load_pks2dbx (); + if (err != GRUB_ERR_NONE) + grub_printf ("warning: dbx list might not be fully populated\n"); + + /* + * If db does not exist in the PKS storage, then read the static keys as a db + * default keys from the GRUB ELF Note and add them into the db list. + */ + if (pks_keystore->db_exists == false) + load_elf2db (); + else + { + err = load_pks2db (); + if (err != GRUB_ERR_NONE) + grub_printf ("warning: db list might not be fully populated\n"); + } + + grub_pks_free_data (); + grub_dprintf ("appendedsig", "the db list now has %u keys\n" + "the dbx list now has %u keys\n", + db.hash_entries + db.cert_entries, + dbx.hash_entries + dbx.cert_entries); +} + /* Free db list memory */ static void free_db_list (void) { grub_x509_cert_t *cert; + grub_uint32_t i; while (db.certs != NULL) { @@ -436,9 +767,37 @@ free_db_list (void) grub_free (cert); } + for (i = 0; i < db.hash_entries; i++) + grub_free (db.hashes[i]); + + grub_free (db.hashes); + grub_free (db.hash_sizes); grub_memset (&db, 0, sizeof (sb_database_t)); } +/* Free dbx list memory */ +static void +free_dbx_list (void) +{ + grub_x509_cert_t *cert; + grub_uint32_t i; + + while (dbx.certs != NULL) + { + cert = dbx.certs; + dbx.certs = dbx.certs->next; + grub_x509_cert_release (cert); + grub_free (cert); + } + + for (i = 0; i < dbx.hash_entries; i++) + grub_free (dbx.hashes[i]); + + grub_free (dbx.hashes); + grub_free (dbx.hash_sizes); + grub_memset (&dbx, 0, sizeof (sb_database_t)); +} + static const char * grub_env_read_sec (struct grub_env_var *var __attribute__ ((unused)), const char *val __attribute__ ((unused))) @@ -631,10 +990,23 @@ GRUB_MOD_INIT (appendedsig) if (rc != ASN1_SUCCESS) grub_fatal ("error initing ASN.1 data structures: %d: %s\n", rc, asn1_strerror (rc)); - /* Extract trusted keys from ELF Note and store them in the db. */ - load_elf2db (); - grub_dprintf ("appendedsig", "the db list now has %u static keys\n", - db.cert_entries); + /* + * If signature verification is enabled with the dynamic key management, + * extract trusted and distrusted keys from PKS and store them in the db + * and dbx list. + */ + if (append_key_mgmt == true) + create_dbs_from_pks (); + /* + * If signature verification is enabled with the static key management, + * extract trusted keys from ELF Note and store them in the db list. + */ + else + { + load_elf2db (); + grub_dprintf ("appendedsig", "the db list now has %u static keys\n", + db.cert_entries); + } grub_verifier_register (&grub_appendedsig_verifier); grub_dl_set_persistent (mod); @@ -648,6 +1020,7 @@ GRUB_MOD_FINI (appendedsig) */ free_db_list (); + free_dbx_list (); grub_register_variable_hook ("check_appended_signatures", NULL, NULL); grub_env_unset ("check_appended_signatures"); grub_register_variable_hook ("appendedsig_key_mgmt", NULL, NULL); diff --git a/include/grub/efi/pks.h b/include/grub/efi/pks.h new file mode 100644 index 000000000..ff306f591 --- /dev/null +++ b/include/grub/efi/pks.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved. This + * program and the accompanying materials are licensed and made available + * under the terms and conditions of the 2-Clause BSD License which + * accompanies this distribution. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * https://github.com/tianocore/edk2-staging (edk2-staging repo of tianocore), + * the ImageAuthentication.h file under it, and here's the copyright and license. + * + * MdePkg/Include/Guid/ImageAuthentication.h + * + * Copyright 2022, 2023, 2024, 2025 IBM Corp. + */ + +#ifndef PKS_HEADER +#define PKS_HEADER 1 + +#include + +/* + * It is derived from EFI_CERT_X509_GUID. + * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h + */ +#define GRUB_PKS_CERT_X509_GUID \ + (grub_guid_t) \ + { 0xa159c0a5, 0xe494, 0xa74a, \ + { 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72 } \ + } + +/* + * It is derived from EFI_CERT_SHA256_GUID. + * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h + */ +#define GRUB_PKS_CERT_SHA256_GUID \ + (grub_guid_t) \ + { 0x2616c4c1, 0x4c50, 0x9240, \ + { 0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28 } \ + } + +/* + * It is derived from EFI_CERT_SHA384_GUID. + * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h + */ +#define GRUB_PKS_CERT_SHA384_GUID \ + (grub_guid_t) \ + { 0x07533eff, 0xd09f, 0xc948, \ + { 0x85, 0xf1, 0x8a, 0xd5, 0x6c, 0x70, 0x1e, 0x1 } \ + } + +/* + * It is derived from EFI_CERT_SHA512_GUID. + * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h + */ +#define GRUB_PKS_CERT_SHA512_GUID \ + (grub_guid_t) \ + { 0xae0f3e09, 0xc4a6, 0x504f, \ + { 0x9f, 0x1b, 0xd4, 0x1e, 0x2b, 0x89, 0xc1, 0x9a } \ + } + +/* + * It is derived from EFI_CERT_X509_SHA256_GUID. + * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h + */ +#define GRUB_PKS_CERT_X509_SHA256_GUID \ + (grub_guid_t) \ + { 0x92a4d23b, 0xc096, 0x7940, \ + { 0xb4, 0x20, 0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed } \ + } + +/* + * It is derived from EFI_CERT_X509_SHA384_GUID. + * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h + */ +#define GRUB_PKS_CERT_X509_SHA384_GUID \ + (grub_guid_t) \ + { 0x6e877670, 0xc280, 0xe64e, \ + { 0xaa, 0xd2, 0x28, 0xb3, 0x49, 0xa6, 0x86, 0x5b } \ + } + +/* + * It is derived from EFI_CERT_X509_SHA512_GUID. + * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h + */ +#define GRUB_PKS_CERT_X509_SHA512_GUID \ + (grub_guid_t) \ + { 0x63bf6d44, 0x0225, 0xda4c, \ + { 0xbc, 0xfa, 0x24, 0x65, 0xd2, 0xb0, 0xfe, 0x9d } \ + } + +#endif diff --git a/include/grub/types.h b/include/grub/types.h index 45079bf65..b3ba762fc 100644 --- a/include/grub/types.h +++ b/include/grub/types.h @@ -379,6 +379,8 @@ struct grub_guid } __attribute__ ((aligned(4))); typedef struct grub_guid grub_guid_t; +#define GRUB_GUID_SIZE (sizeof (grub_guid_t)) + struct grub_packed_guid { grub_uint32_t data1; @@ -388,4 +390,6 @@ struct grub_packed_guid } GRUB_PACKED; typedef struct grub_packed_guid grub_packed_guid_t; +#define GRUB_PACKED_GUID_SIZE (sizeof (grub_packed_guid_t)) + #endif /* ! GRUB_TYPES_HEADER */ From ab7b1771784052417654cab1f34eff00f1839397 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:54:59 +0530 Subject: [PATCH 046/208] appended signatures: Using db and dbx lists for signature verification Signature verification: verify the kernel against lists of hashes that are either in dbx or db list. If it is not in the dbx list then the trusted keys from the db list are used to verify the signature. Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/commands/appendedsig/appendedsig.c | 94 +++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c index 0c4c788ff..9cfa1bebb 100644 --- a/grub-core/commands/appendedsig/appendedsig.c +++ b/grub-core/commands/appendedsig/appendedsig.c @@ -521,6 +521,83 @@ extract_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize, return grub_pkcs7_data_parse (signed_data, appendedsig_pkcs7_size, &sig->pkcs7); } +static grub_err_t +get_binary_hash (const grub_size_t binary_hash_size, const grub_uint8_t *data, + const grub_size_t data_size, grub_uint8_t *hash, grub_size_t *hash_size) +{ + grub_packed_guid_t guid = { 0 }; + + /* support SHA256, SHA384 and SHA512 for binary hash */ + if (binary_hash_size == SHA256_HASH_SIZE) + grub_memcpy (&guid, &GRUB_PKS_CERT_SHA256_GUID, GRUB_PACKED_GUID_SIZE); + else if (binary_hash_size == SHA384_HASH_SIZE) + grub_memcpy (&guid, &GRUB_PKS_CERT_SHA384_GUID, GRUB_PACKED_GUID_SIZE); + else if (binary_hash_size == SHA512_HASH_SIZE) + grub_memcpy (&guid, &GRUB_PKS_CERT_SHA512_GUID, GRUB_PACKED_GUID_SIZE); + else + { + grub_dprintf ("appendedsig", "unsupported hash type (%" PRIuGRUB_SIZE ") and " + "skipped\n", binary_hash_size); + return GRUB_ERR_UNKNOWN_COMMAND; + } + + return get_hash (&guid, data, data_size, hash, hash_size); +} + +/* + * Verify binary hash against the db and dbx list. + * The following errors can occur: + * - GRUB_ERR_BAD_SIGNATURE: indicates that the hash is in dbx list. + * - GRUB_ERR_EOF: the hash could not be found in the db and dbx list. + * - GRUB_ERR_NONE: the hash is found in db list. + */ +static grub_err_t +verify_binary_hash (const grub_uint8_t *data, const grub_size_t data_size) +{ + grub_err_t rc = GRUB_ERR_NONE; + grub_uint32_t i; + grub_size_t hash_size = 0; + grub_uint8_t hash[GRUB_MAX_HASH_LEN] = { 0 }; + + for (i = 0; i < dbx.hash_entries; i++) + { + if (dbx.hashes[i] == NULL) + continue; + + rc = get_binary_hash (dbx.hash_sizes[i], data, data_size, hash, &hash_size); + if (rc != GRUB_ERR_NONE) + continue; + + if (hash_size == dbx.hash_sizes[i] && + grub_memcmp (dbx.hashes[i], hash, hash_size) == 0) + { + grub_dprintf ("appendedsig", "the hash (%02x%02x%02x%02x) is present in the dbx list\n", + hash[0], hash[1], hash[2], hash[3]); + return GRUB_ERR_BAD_SIGNATURE; + } + } + + for (i = 0; i < db.hash_entries; i++) + { + if (db.hashes[i] == NULL) + continue; + + rc = get_binary_hash (db.hash_sizes[i], data, data_size, hash, &hash_size); + if (rc != GRUB_ERR_NONE) + continue; + + if (hash_size == db.hash_sizes[i] && + grub_memcmp (db.hashes[i], hash, hash_size) == 0) + { + grub_dprintf ("appendedsig", "verified with a trusted hash (%02x%02x%02x%02x)\n", + hash[0], hash[1], hash[2], hash[3]); + return GRUB_ERR_NONE; + } + } + + return GRUB_ERR_EOF; +} + /* * Given a hash value 'hval', of hash specification 'hash', prepare the * S-expressions (sexp) and perform the signature verification. @@ -565,7 +642,7 @@ grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize) grub_pkcs7_signer_t *si; grub_int32_t i; - if (!db.cert_entries) + if (!db.cert_entries && !db.hash_entries) return grub_error (GRUB_ERR_BAD_SIGNATURE, "no trusted keys to verify against"); err = extract_appended_signature (buf, bufsize, &sig); @@ -574,6 +651,21 @@ grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize) datasize = bufsize - sig.signature_len; + /* + * If signature verification is enabled with dynamic key management mode, + * Verify binary hash against the db and dbx list. + */ + if (append_key_mgmt == true) + { + err = verify_binary_hash (buf, datasize); + if (err == GRUB_ERR_BAD_SIGNATURE) + { + grub_pkcs7_data_release (&sig.pkcs7); + return grub_error (err, + "failed to verify the binary hash against a trusted binary hash"); + } + } + /* Verify signature using trusted keys from db list. */ for (i = 0; i < sig.pkcs7.signer_count; i++) { From 6cb58b1c9e045c1ecf89e09592f9dabbdd4fd1a6 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:55:00 +0530 Subject: [PATCH 047/208] appended signatures: GRUB commands to manage the certificates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introducing the following GRUB commands to manage the certificates. 1. append_list_db: Show the list of trusted certificates from the db list 2. append_add_db_cert: Add the trusted certificate to the db list 3. append_add_dbx_cert: Add the distrusted certificate to the dbx list 4. append_verify: Verify the signed file using db list Note that if signature verification (check_appended_signatures) is set to yes, the append_add_db_cert and append_add_dbx_cert commands only accept the file ‘X509_certificate’ that is signed with an appended signature. Signed-off-by: Daniel Axtens Signed-off-by: Sudhakar Kuppusamy Tested-by: Sridhar Markonda Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/commands/appendedsig/appendedsig.c | 316 +++++++++++++++++++ 1 file changed, 316 insertions(+) diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c index 9cfa1bebb..614ebeec0 100644 --- a/grub-core/commands/appendedsig/appendedsig.c +++ b/grub-core/commands/appendedsig/appendedsig.c @@ -123,6 +123,9 @@ static bool append_key_mgmt = false; /* Platform KeyStore db and dbx. */ static grub_pks_t *pks_keystore; +/* Appended signature size. */ +static grub_size_t append_sig_len = 0; + static grub_ssize_t pseudo_read (struct grub_file *file, char *buf, grub_size_t len) { @@ -136,6 +139,65 @@ static struct grub_fs pseudo_fs = { .fs_read = pseudo_read }; +/* + * We cannot use hexdump() to display hash data because it is typically displayed + * in hexadecimal format, along with an ASCII representation of the same data. + * + * Example: sha256 hash data + * 00000000 52 b5 90 49 64 de 22 d7 4e 5f 4f b4 1b 51 9c 34 |R..Id.".N_O..Q.4| + * 00000010 b1 96 21 7c 91 78 a5 0d 20 8c e9 5c 22 54 53 f7 |..!|.x.. ..\"TS.| + * + * An appended signature only required to display the hexadecimal of the hash data + * by separating each byte with ":". So, we introduced a new method hexdump_colon + * to display it. + * + * Example: Sha256 hash data + * 52:b5:90:49:64:de:22:d7:4e:5f:4f:b4:1b:51:9c:34: + * b1:96:21:7c:91:78:a5:0d:20:8c:e9:5c:22:54:53:f7 + */ +static void +hexdump_colon (const grub_uint8_t *data, const grub_size_t length) +{ + grub_size_t i, count = 0; + + for (i = 0; i < length - 1; i++) + { + grub_printf ("%02x:", data[i]); + count++; + if (count == 16) + { + grub_printf ("\n "); + count = 0; + } + } + + grub_printf ("%02x\n", data[i]); +} + +static void +print_certificate (const grub_x509_cert_t *cert, const grub_uint32_t cert_num) +{ + grub_uint32_t i; + + grub_printf ("\nCertificate: %u\n", cert_num); + grub_printf (" Data:\n"); + grub_printf (" Version: %u (0x%u)\n", cert->version + 1, cert->version); + grub_printf (" Serial Number:\n "); + + for (i = 0; i < cert->serial_len - 1; i++) + grub_printf ("%02x:", cert->serial[i]); + + grub_printf ("%02x\n", cert->serial[cert->serial_len - 1]); + grub_printf (" Issuer: %s\n", cert->issuer); + grub_printf (" Subject: %s\n", cert->subject); + grub_printf (" Subject Public Key Info:\n"); + grub_printf (" Public Key Algorithm: rsaEncryption\n"); + grub_printf (" RSA Public-Key: (%d bit)\n", cert->modulus_size); + grub_printf (" Fingerprint: sha256\n "); + hexdump_colon (&cert->fingerprint[GRUB_FINGERPRINT_SHA256][0], + grub_strlen ((char *) cert->fingerprint[GRUB_FINGERPRINT_SHA256])); +} + /* * GUID can be used to determine the hashing function and generate the hash using * determined hashing function. @@ -429,6 +491,61 @@ add_certificate (const grub_uint8_t *data, const grub_size_t data_size, return rc; } +static void +_remove_cert_from_db (const grub_x509_cert_t *cert) +{ + grub_uint32_t i = 1; + grub_x509_cert_t *curr_cert, *prev_cert; + + for (curr_cert = prev_cert = db.certs; curr_cert != NULL; curr_cert = curr_cert->next, i++) + { + if (is_cert_match (curr_cert, cert) == true) + { + if (i == 1) /* Match with first certificate in the db list. */ + db.certs = curr_cert->next; + else + prev_cert->next = curr_cert->next; + + grub_dprintf ("appendedsig", + "removed distrusted certificate with CN: %s from the db list\n", + curr_cert->subject); + curr_cert->next = NULL; + grub_x509_cert_release (curr_cert); + grub_free (curr_cert); + break; + } + else + prev_cert = curr_cert; + } +} + +static grub_err_t +remove_cert_from_db (const grub_uint8_t *data, const grub_size_t data_size) +{ + grub_err_t rc; + grub_x509_cert_t *cert; + + if (data == NULL || data_size == 0) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "certificate data or size is not available"); + + cert = grub_zalloc (sizeof (grub_x509_cert_t)); + if (cert == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + + rc = grub_x509_cert_parse (data, data_size, cert); + if (rc != GRUB_ERR_NONE) + { + grub_dprintf ("appendedsig", "cannot remove an invalid certificate from the db list\n"); + grub_free (cert); + return rc; + } + + /* Remove certificate from the db list. */ + _remove_cert_from_db (cert); + + return rc; +} + static grub_err_t file_read_whole (grub_file_t file, grub_uint8_t **buf, grub_size_t *len) { @@ -649,6 +766,7 @@ grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize) if (err != GRUB_ERR_NONE) return err; + append_sig_len = sig.signature_len; datasize = bufsize - sig.signature_len; /* @@ -709,6 +827,189 @@ grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize) return err; } +static grub_err_t +grub_cmd_verify_signature (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) +{ + grub_file_t signed_file; + grub_err_t err; + grub_uint8_t *signed_data = NULL; + grub_size_t signed_data_size = 0; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "a signed file is expected\nExample:\n\tappend_verify \n"); + + if (!grub_strlen (args[0])) + return grub_error (GRUB_ERR_BAD_FILENAME, "missing signed file"); + + grub_dprintf ("appendedsig", "verifying %s\n", args[0]); + + signed_file = grub_file_open (args[0], GRUB_FILE_TYPE_VERIFY_SIGNATURE); + if (signed_file == NULL) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "could not open %s file", args[0]); + + err = file_read_whole (signed_file, &signed_data, &signed_data_size); + if (err == GRUB_ERR_NONE) + { + err = grub_verify_appended_signature (signed_data, signed_data_size); + grub_free (signed_data); + } + + grub_file_close (signed_file); + + return err; +} + +/* + * Checks the trusted certificate against dbx list if dynamic key management is + * enabled. And add it to the db list if it is not already present. + * + * Note: When signature verification is enabled, this command only accepts the + * trusted certificate that is signed with an appended signature. + * The signature is verified by the appendedsig module. If verification succeeds, + * the certificate is added to the db list. Otherwise, an error is posted and + * the certificate is not added. + * When signature verification is disabled, it accepts the trusted certificate + * without an appended signature and add it to the db list. + * + * Also, note that the adding of the trusted certificate using this command does + * not persist across reboots. + */ +static grub_err_t +grub_cmd_db_cert (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) +{ + grub_err_t err; + grub_file_t cert_file; + grub_uint8_t *cert_data = NULL; + grub_size_t cert_data_size = 0; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "a trusted X.509 certificate file is expected in DER format\n" + "Example:\n\tappend_add_db_cert \n"); + + if (!grub_strlen (args[0])) + return grub_error (GRUB_ERR_BAD_FILENAME, "missing trusted X.509 certificate file"); + + cert_file = grub_file_open (args[0], + GRUB_FILE_TYPE_CERTIFICATE_TRUST | GRUB_FILE_TYPE_NO_DECOMPRESS); + if (cert_file == NULL) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "could not open %s file", args[0]); + + err = file_read_whole (cert_file, &cert_data, &cert_data_size); + grub_file_close (cert_file); + if (err != GRUB_ERR_NONE) + return err; + + /* + * If signature verification is enabled (check_sigs is set to true), obtain + * the actual certificate size by subtracting the appended signature size from + * the certificate size because the certificate has an appended signature, and + * this actual certificate size is used to get the X.509 certificate. + */ + if (check_sigs == true) + cert_data_size -= append_sig_len; + + err = add_certificate (cert_data, cert_data_size, &db); + grub_free (cert_data); + + return err; +} + +/* + * Remove the distrusted certificate from the db list if it is already present. + * And add it to the dbx list if not present when dynamic key management is + * enabled. + * + * Note: When signature verification is enabled, this command only accepts the + * distrusted certificate that is signed with an appended signature. + * The signature is verified by the appended sig module. If verification + * succeeds, the certificate is removed from the db list. Otherwise, an error + * is posted and the certificate is not removed. + * When signature verification is disabled, it accepts the distrusted certificate + * without an appended signature and removes it from the db list. + * + * Also, note that the removal of the distrusted certificate using this command + * does not persist across reboots. + */ +static grub_err_t +grub_cmd_dbx_cert (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) +{ + grub_err_t err; + grub_file_t cert_file; + grub_uint8_t *cert_data = NULL; + grub_size_t cert_data_size = 0; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "a distrusted X.509 certificate file is expected in DER format\n" + "Example:\n\tappend_add_dbx_cert \n"); + + if (!grub_strlen (args[0])) + return grub_error (GRUB_ERR_BAD_FILENAME, "missing distrusted X.509 certificate file"); + + cert_file = grub_file_open (args[0], + GRUB_FILE_TYPE_CERTIFICATE_TRUST | GRUB_FILE_TYPE_NO_DECOMPRESS); + if (cert_file == NULL) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "could not open %s file", args[0]); + + err = file_read_whole (cert_file, &cert_data, &cert_data_size); + grub_file_close (cert_file); + if (err != GRUB_ERR_NONE) + return err; + + /* + * If signature verification is enabled (check_sigs is set to true), obtain + * the actual certificate size by subtracting the appended signature size from + * the certificate size because the certificate has an appended signature, and + * this actual certificate size is used to get the X.509 certificate. + */ + if (check_sigs == true) + cert_data_size -= append_sig_len; + + /* Remove distrusted certificate from the db list if present. */ + err = remove_cert_from_db (cert_data, cert_data_size); + if (err != GRUB_ERR_NONE) + { + grub_free (cert_data); + return err; + } + + /* Only add the certificate to the dbx list if dynamic key management is enabled. */ + if (append_key_mgmt == true) + err = add_certificate (cert_data, cert_data_size, &dbx); + + grub_free (cert_data); + + return err; +} + +static grub_err_t +grub_cmd_list_db (grub_command_t cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + struct x509_certificate *cert; + grub_uint32_t i, cert_num = 1; + + for (cert = db.certs; cert != NULL; cert = cert->next, cert_num++) + print_certificate (cert, cert_num); + + if (append_key_mgmt == false) + return GRUB_ERR_NONE; + + for (i = 0; i < db.hash_entries; i++) + { + if (db.hashes[i] != NULL) + { + grub_printf ("\nBinary hash: %u\n", i + 1); + grub_printf (" Hash: sha%" PRIuGRUB_SIZE "\n ", db.hash_sizes[i] * 8); + hexdump_colon (db.hashes[i], db.hash_sizes[i]); + } + } + + return GRUB_ERR_NONE; +} + /* Add the X.509 certificates/binary hash to the db list from PKS. */ static grub_err_t load_pks2db (void) @@ -1031,6 +1332,8 @@ struct grub_file_verifier grub_appendedsig_verifier = { .write = appendedsig_write, }; +static grub_command_t cmd_verify, cmd_list_db, cmd_dbx_cert, cmd_db_cert; + GRUB_MOD_INIT (appendedsig) { grub_int32_t rc; @@ -1100,6 +1403,15 @@ GRUB_MOD_INIT (appendedsig) db.cert_entries); } + cmd_verify = grub_register_command ("append_verify", grub_cmd_verify_signature, N_(""), + N_("Verify SIGNED_FILE against the trusted X.509 certificates in the db list")); + cmd_list_db = grub_register_command ("append_list_db", grub_cmd_list_db, 0, + N_("Show the list of trusted X.509 certificates from the db list")); + cmd_db_cert = grub_register_command ("append_add_db_cert", grub_cmd_db_cert, N_(""), + N_("Add trusted X509_CERTIFICATE to the db list")); + cmd_dbx_cert = grub_register_command ("append_add_dbx_cert", grub_cmd_dbx_cert, N_(""), + N_("Add distrusted X509_CERTIFICATE to the dbx list")); + grub_verifier_register (&grub_appendedsig_verifier); grub_dl_set_persistent (mod); } @@ -1118,4 +1430,8 @@ GRUB_MOD_FINI (appendedsig) grub_register_variable_hook ("appendedsig_key_mgmt", NULL, NULL); grub_env_unset ("appendedsig_key_mgmt"); grub_verifier_unregister (&grub_appendedsig_verifier); + grub_unregister_command (cmd_verify); + grub_unregister_command (cmd_list_db); + grub_unregister_command (cmd_db_cert); + grub_unregister_command (cmd_dbx_cert); } From 7f68c7195221eba9f8f2b0c772bb2abd37771fc7 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:55:01 +0530 Subject: [PATCH 048/208] appended signatures: GRUB commands to manage the hashes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introducing the following GRUB commands to manage certificate/binary hashes. 1. append_list_dbx: Show the list of distrusted certificates and binary/certificate hashes from the dbx list. 2. append_add_db_hash: Add the trusted binary hash to the db list. 3. append_add_dbx_hash: Add the distrusted certificate/binary hash to the dbx list. Note that if signature verification (check_appended_signatures) is set to yes, the append_add_db_hash and append_add_dbx_hash commands only accept the file ‘hash_file’ that is signed with an appended signature. Signed-off-by: Sudhakar Kuppusamy Tested-by: Sridhar Markonda Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/commands/appendedsig/appendedsig.c | 279 +++++++++++++++++++ include/grub/file.h | 2 + 2 files changed, 281 insertions(+) diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c index 614ebeec0..5c53f634c 100644 --- a/grub-core/commands/appendedsig/appendedsig.c +++ b/grub-core/commands/appendedsig/appendedsig.c @@ -52,6 +52,9 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define SHA384_HASH_SIZE 48 #define SHA512_HASH_SIZE 64 +#define OPTION_BINARY_HASH 0 +#define OPTION_CERT_HASH 1 + /* * This structure is extracted from scripts/sign-file.c in the linux kernel * source. It was licensed as LGPLv2.1+, which is GPLv3+ compatible. @@ -126,6 +129,13 @@ static grub_pks_t *pks_keystore; /* Appended signature size. */ static grub_size_t append_sig_len = 0; +static const struct grub_arg_option options[] = +{ + {"binary-hash", 'b', 0, N_("hash file of the binary."), 0, ARG_TYPE_PATHNAME}, + {"cert-hash", 'c', 1, N_("hash file of the certificate."), 0, ARG_TYPE_PATHNAME}, + {0, 0, 0, 0, 0, 0} +}; + static grub_ssize_t pseudo_read (struct grub_file *file, char *buf, grub_size_t len) { @@ -546,6 +556,69 @@ remove_cert_from_db (const grub_uint8_t *data, const grub_size_t data_size) return rc; } +static bool +cert_fingerprint_match (const grub_uint8_t *hash_data, const grub_size_t hash_data_size, + const grub_x509_cert_t *cert) +{ + grub_int32_t type; + + if (hash_data_size == SHA256_HASH_SIZE) + type = GRUB_FINGERPRINT_SHA256; + else if (hash_data_size == SHA384_HASH_SIZE) + type = GRUB_FINGERPRINT_SHA384; + else if (hash_data_size == SHA512_HASH_SIZE) + type = GRUB_FINGERPRINT_SHA512; + else + { + grub_dprintf ("appendedsig", "unsupported fingerprint hash type " + "(%" PRIuGRUB_SIZE ") \n", hash_data_size); + return false; + } + + if (grub_memcmp (cert->fingerprint[type], hash_data, hash_data_size) == 0) + return true; + + return false; +} + +static void +remove_hash_from_db (const grub_uint8_t *hash_data, const grub_size_t hash_data_size, + const bool bin_hash) +{ + grub_uint32_t i; + grub_x509_cert_t *cert; + + if (bin_hash == true) + { + for (i = 0; i < db.hash_entries; i++) + { + if (db.hashes[i] == NULL) + continue; + + if (grub_memcmp (db.hashes[i], hash_data, hash_data_size) == 0) + { + grub_dprintf ("appendedsig", "removed distrusted hash %02x%02x%02x%02x.. from the db list\n", + db.hashes[i][0], db.hashes[i][1], db.hashes[i][2], db.hashes[i][3]); + grub_free (db.hashes[i]); + db.hashes[i] = NULL; + db.hash_sizes[i] = 0; + break; + } + } + } + else + { + for (cert = db.certs; cert != NULL; cert = cert->next) + { + if (cert_fingerprint_match (hash_data, hash_data_size, cert) == true) + { + _remove_cert_from_db (cert); + break; + } + } + } +} + static grub_err_t file_read_whole (grub_file_t file, grub_uint8_t **buf, grub_size_t *len) { @@ -1010,6 +1083,192 @@ grub_cmd_list_db (grub_command_t cmd __attribute__ ((unused)), int argc __attrib return GRUB_ERR_NONE; } +static grub_err_t +grub_cmd_list_dbx (grub_command_t cmd __attribute__((unused)), + int argc __attribute__((unused)), char **args __attribute__((unused))) +{ + struct x509_certificate *cert; + grub_uint32_t i, cert_num = 1; + + if (append_key_mgmt == false) + return grub_error (GRUB_ERR_ACCESS_DENIED, + "append_list_dbx command is unsupported in static key mode"); + + for (cert = dbx.certs; cert != NULL; cert = cert->next, cert_num++) + print_certificate (cert, cert_num); + + for (i = 0; i < dbx.hash_entries; i++) + { + if (dbx.hashes[i] != NULL) + { + grub_printf ("\nCertificate/Binary hash: %u\n", i + 1); + grub_printf (" Hash: sha%" PRIuGRUB_SIZE "\n ", dbx.hash_sizes[i] * 8); + hexdump_colon (dbx.hashes[i], dbx.hash_sizes[i]); + } + } + + return GRUB_ERR_NONE; +} + +/* + * Remove the trusted binary hash from the dbx list if present. And add them to + * the db list if it is not already present. + * + * Note: When signature verification is enabled, this command only accepts the + * binary hash file that is signed with an appended signature. The signature is + * verified by the appendedsig module. If verification succeeds, the binary hash + * is added to the db list. Otherwise, an error is posted and the binary hash is + * not added. + * When signature verification is disabled, it accepts the binary hash file + * without an appended signature and adds it to the db list. + * + * Also, note that the adding of the trusted binary hash using this command does + * not persist across reboots. + */ +static grub_err_t +grub_cmd_add_db_hash (grub_command_t cmd __attribute__((unused)), int argc, char**args) +{ + grub_err_t rc; + grub_file_t hash_file; + grub_uint8_t *hash_data = NULL; + grub_size_t hash_data_size = 0; + + if (append_key_mgmt == false) + return grub_error (GRUB_ERR_ACCESS_DENIED, + "append_add_db_hash command is unsupported in static key mode"); + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "a trusted binary hash file is expected in binary format\n" + "Example:\n\tappend_add_db_hash \n"); + + if (!grub_strlen (args[0])) + return grub_error (GRUB_ERR_BAD_FILENAME, "missing trusted binary hash file"); + + hash_file = grub_file_open (args[0], GRUB_FILE_TYPE_HASH_TRUST | GRUB_FILE_TYPE_NO_DECOMPRESS); + if (hash_file == NULL) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "unable to open %s file", args[0]); + + rc = file_read_whole (hash_file, &hash_data, &hash_data_size); + grub_file_close (hash_file); + if (rc != GRUB_ERR_NONE) + return rc; + + /* + * If signature verification is enabled (check_sigs is set to true), obtain + * the actual hash data size by subtracting the appended signature size from + * the hash data size because the hash has an appended signature, and this + * actual hash data size is used to get the hash data. + */ + if (check_sigs == true) + hash_data_size -= append_sig_len; + + grub_dprintf ("appendedsig", + "adding a trusted binary hash %02x%02x%02x%02x... with size of %" PRIuGRUB_SIZE "\n", + hash_data[0], hash_data[1], hash_data[2], hash_data[3], hash_data_size); + + /* Only accept SHA256, SHA384 and SHA512 binary hash */ + if (hash_data_size != SHA256_HASH_SIZE && hash_data_size != SHA384_HASH_SIZE && + hash_data_size != SHA512_HASH_SIZE) + { + grub_free (hash_data); + return grub_error (GRUB_ERR_BAD_SIGNATURE, "unacceptable trusted binary hash type"); + } + + rc = add_hash (hash_data, hash_data_size, &db); + grub_free (hash_data); + + return rc; +} + +/* + * Remove the distrusted binary/certificate hash from the db list if present. + * And add them to the dbx list if it is not already present. + * + * Note: When signature verification is enabled, this command only accepts the + * binary/certificate hash file that is signed with an appended signature. The + * signature is verified by the appendedsig module. If verification succeeds, + * the binary/certificate hash is added to the dbx list. Otherwise, an error is + * posted and the binary/certificate hash is not added. + * When signature verification is disabled, it accepts the binary/certificate + * hash file without an appended signature and adds it to the dbx list. + * + * Also, note that the adding of the distrusted binary/certificate hash using + * this command does not persist across reboots. + */ +static grub_err_t +grub_cmd_add_dbx_hash (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + grub_err_t rc; + grub_file_t hash_file; + grub_uint8_t *hash_data = NULL; + grub_size_t hash_data_size = 0; + char *file_path; + + if (append_key_mgmt == false) + return grub_error (GRUB_ERR_ACCESS_DENIED, + "append_add_dbx_hash command is unsupported in static key mode"); + + if (!ctxt->state[OPTION_BINARY_HASH].set && !ctxt->state[OPTION_CERT_HASH].set) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "a distrusted certificate/binary hash file is expected in binary format\n" + "Example:\n\tappend_add_dbx_hash [option] \n" + "option:\n[-b|--binary-hash] FILE [BINARY HASH FILE]\n" + "[-c|--cert-hash] FILE [CERTFICATE HASH FILE]\n"); + + if (ctxt->state[OPTION_BINARY_HASH].arg == NULL && ctxt->state[OPTION_CERT_HASH].arg == NULL) + return grub_error (GRUB_ERR_BAD_FILENAME, "missing distrusted certificate/binary hash file"); + + if (ctxt->state[OPTION_BINARY_HASH].arg != NULL) + file_path = ctxt->state[OPTION_BINARY_HASH].arg; + else + file_path = ctxt->state[OPTION_CERT_HASH].arg; + + hash_file = grub_file_open (file_path, GRUB_FILE_TYPE_HASH_TRUST | GRUB_FILE_TYPE_NO_DECOMPRESS); + if (hash_file == NULL) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "unable to open %s file", file_path); + + rc = file_read_whole (hash_file, &hash_data, &hash_data_size); + grub_file_close (hash_file); + if (rc != GRUB_ERR_NONE) + return rc; + + /* + * If signature verification is enabled (check_sigs is set to true), obtain + * the actual hash data size by subtracting the appended signature size from + * the hash data size because the hash has an appended signature, and this + * actual hash data size is used to get the hash data. + */ + if (check_sigs == true) + hash_data_size -= append_sig_len; + + grub_dprintf ("appendedsig", + "adding a distrusted certificate/binary hash %02x%02x%02x%02x..." + " with size of %" PRIuGRUB_SIZE "\n", hash_data[0], hash_data[1], + hash_data[2], hash_data[3], hash_data_size); + + if (ctxt->state[OPTION_BINARY_HASH].set || ctxt->state[OPTION_CERT_HASH].set) + { + /* Only accept SHA256, SHA384 and SHA512 certificate/binary hash */ + if (hash_data_size != SHA256_HASH_SIZE && hash_data_size != SHA384_HASH_SIZE && + hash_data_size != SHA512_HASH_SIZE) + { + grub_free (hash_data); + return grub_error (GRUB_ERR_BAD_SIGNATURE, + "unacceptable distrusted certificate/binary hash type"); + } + } + + /* Remove distrusted binary hash/certificate from the db list if present. */ + remove_hash_from_db (hash_data, hash_data_size, + (ctxt->state[OPTION_BINARY_HASH].set) ? true : false); + rc = add_hash (hash_data, hash_data_size, &dbx); + grub_free (hash_data); + + return rc; +} + /* Add the X.509 certificates/binary hash to the db list from PKS. */ static grub_err_t load_pks2db (void) @@ -1292,6 +1551,11 @@ appendedsig_init (grub_file_t io __attribute__ ((unused)), enum grub_file_type t * verifier, but we lack the hubris required to take this on. Instead, * require that it have an appended signature. */ + case GRUB_FILE_TYPE_HASH_TRUST: + /* + * This is a certificate/binary hash to add to db/dbx. This needs to be + * verified or blocked. + */ case GRUB_FILE_TYPE_LINUX_KERNEL: case GRUB_FILE_TYPE_GRUB_MODULE: /* @@ -1333,6 +1597,8 @@ struct grub_file_verifier grub_appendedsig_verifier = { }; static grub_command_t cmd_verify, cmd_list_db, cmd_dbx_cert, cmd_db_cert; +static grub_command_t cmd_list_dbx, cmd_db_hash; +static grub_extcmd_t cmd_dbx_hash; GRUB_MOD_INIT (appendedsig) { @@ -1412,6 +1678,16 @@ GRUB_MOD_INIT (appendedsig) cmd_dbx_cert = grub_register_command ("append_add_dbx_cert", grub_cmd_dbx_cert, N_(""), N_("Add distrusted X509_CERTIFICATE to the dbx list")); + cmd_list_dbx = grub_register_command ("append_list_dbx", grub_cmd_list_dbx, 0, + N_("Show the list of distrusted certificates and" + " certificate/binary hashes from the dbx list")); + cmd_db_hash = grub_register_command ("append_add_db_hash", grub_cmd_add_db_hash, N_("BINARY HASH FILE"), + N_("Add trusted BINARY HASH to the db list.")); + cmd_dbx_hash = grub_register_extcmd ("append_add_dbx_hash", grub_cmd_add_dbx_hash, 0, + N_("[-b|--binary-hash] FILE [BINARY HASH FILE]\n" + "[-c|--cert-hash] FILE [CERTFICATE HASH FILE]"), + N_("Add distrusted CERTFICATE/BINARY HASH to the dbx list."), options); + grub_verifier_register (&grub_appendedsig_verifier); grub_dl_set_persistent (mod); } @@ -1434,4 +1710,7 @@ GRUB_MOD_FINI (appendedsig) grub_unregister_command (cmd_list_db); grub_unregister_command (cmd_db_cert); grub_unregister_command (cmd_dbx_cert); + grub_unregister_command (cmd_list_dbx); + grub_unregister_command (cmd_db_hash); + grub_unregister_extcmd (cmd_dbx_hash); } diff --git a/include/grub/file.h b/include/grub/file.h index d678de063..16a4b7d26 100644 --- a/include/grub/file.h +++ b/include/grub/file.h @@ -115,6 +115,8 @@ enum grub_file_type GRUB_FILE_TYPE_HASHLIST, /* File hashed by hashsum. */ GRUB_FILE_TYPE_TO_HASH, + /* File holding certificiate/binary hash to add to db/dbx. */ + GRUB_FILE_TYPE_HASH_TRUST, /* Keyboard layout. */ GRUB_FILE_TYPE_KEYBOARD_LAYOUT, /* Picture file. */ From dbfa3d7d7ef0cda5cb6149a372a132b30925d97e Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:55:02 +0530 Subject: [PATCH 049/208] appended signatures: Verification tests These tests are run through all_functional_test and test a range of commands and behaviours. Signed-off-by: Daniel Axtens Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/Makefile.core.def | 8 + grub-core/tests/appended_signature_test.c | 348 ++++++++ grub-core/tests/appended_signatures.h | 975 ++++++++++++++++++++++ grub-core/tests/lib/functional_test.c | 1 + 4 files changed, 1332 insertions(+) create mode 100644 grub-core/tests/appended_signature_test.c create mode 100644 grub-core/tests/appended_signatures.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 853d879a4..a729cb6a8 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2231,6 +2231,14 @@ module = { cppflags = '$(CPPFLAGS_GCRY)'; }; +module = { + name = appended_signature_test; + common = tests/appended_signature_test.c; + common = tests/appended_signatures.h; + enable = emu; + enable = powerpc_ieee1275; +}; + module = { name = signature_test; common = tests/signature_test.c; diff --git a/grub-core/tests/appended_signature_test.c b/grub-core/tests/appended_signature_test.c new file mode 100644 index 000000000..2130008bc --- /dev/null +++ b/grub-core/tests/appended_signature_test.c @@ -0,0 +1,348 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020, 2022 Free Software Foundation, Inc. + * Copyright (C) 2020, 2022, 2025 IBM Corporation + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "appended_signatures.h" + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define PROC_FILE(identifier, file_name) \ + static char *get_##identifier (grub_size_t *sz) \ + { \ + char *ret; \ + \ + *sz = identifier##_len; \ + ret = grub_malloc (*sz); \ + if (ret != NULL) \ + grub_memcpy (ret, identifier, *sz); \ + return ret; \ + } \ + \ + static struct grub_procfs_entry identifier##_entry = { .name = file_name, \ + .get_contents = get_##identifier }; + +#define DEFINE_TEST_CASE(case_name) PROC_FILE (case_name, #case_name) + +#define DO_TEST(case_name, is_valid) \ + { \ + grub_procfs_register (#case_name, &case_name##_entry); \ + do_verify ("(proc)/" #case_name, is_valid); \ + grub_procfs_unregister (&case_name##_entry); \ + } + +DEFINE_TEST_CASE (hi_signed); +DEFINE_TEST_CASE (hi_signed_sha256); +DEFINE_TEST_CASE (hj_signed); +DEFINE_TEST_CASE (short_msg); +DEFINE_TEST_CASE (unsigned_msg); +DEFINE_TEST_CASE (hi_signed_2nd); +DEFINE_TEST_CASE (hi_double); +DEFINE_TEST_CASE (hi_double_extended); + +PROC_FILE (certificate_der, "certificate.der") +PROC_FILE (certificate2_der, "certificate2.der") +PROC_FILE (certificate_printable_der, "certificate_printable.der") +PROC_FILE (certificate_eku_der, "certificate_eku.der") + +static void +do_verify (const char *f, int is_valid) +{ + grub_command_t cmd; + char *args[] = { (char *) f, NULL }; + grub_err_t err; + + cmd = grub_command_find ("append_verify"); + if (cmd == NULL) + { + grub_test_assert (0, "can't find command `%s'", "append_verify"); + return; + } + + err = (cmd->func) (cmd, 1, args); + if (is_valid) + { + grub_test_assert (err == GRUB_ERR_NONE, "verification of %s failed: %d: %s", + f, grub_errno, grub_errmsg); + } + else + { + grub_test_assert (err != GRUB_ERR_NONE, + "verification of %s unexpectedly succeeded", f); + } +} + +static void +appended_signature_test (void) +{ + grub_command_t cmd_trust, cmd_distrust; + char *trust_args[] = { (char *) "(proc)/certificate.der", NULL }; + char *trust_args2[] = { (char *) "(proc)/certificate2.der", NULL }; + char *trust_args_printable[] = { (char *) "(proc)/certificate_printable.der", NULL }; + char *trust_args_eku[] = { (char *) "(proc)/certificate_eku.der", NULL }; + const char *key_mgmt; + grub_err_t err; + + grub_procfs_register ("certificate.der", &certificate_der_entry); + grub_procfs_register ("certificate2.der", &certificate2_der_entry); + grub_procfs_register ("certificate_printable.der", &certificate_printable_der_entry); + grub_procfs_register ("certificate_eku.der", &certificate_eku_der_entry); + + /* Set appended signature key managment to static. */ + err = grub_env_set ("appendedsig_key_mgmt", "static"); + grub_test_assert (err == GRUB_ERR_NONE, "set of key management is failed: %d: %s", + grub_errno, grub_errmsg); + + /* Get appended signatures key management. */ + key_mgmt = grub_env_get ("appendedsig_key_mgmt"); + grub_test_assert (grub_strncmp (key_mgmt, "static", grub_strlen(key_mgmt)) == 0, + "getting unexpected key management: %d: %s", + grub_errno, grub_errmsg); + + cmd_trust = grub_command_find ("append_add_db_cert"); + if (cmd_trust == NULL) + { + grub_test_assert (0, "can't find command `%s'", "append_add_db_cert"); + return; + } + + grub_errno = GRUB_ERR_NONE; + err = (cmd_trust->func) (cmd_trust, 1, trust_args); + grub_test_assert (err == GRUB_ERR_NONE, "loading certificate failed: %d: %s", + grub_errno, grub_errmsg); + /* If we have no certificate the remainder of the tests are meaningless. */ + if (err != GRUB_ERR_NONE) + return; + + /* + * Reload the command: this works around some 'interesting' behaviour in the + * dynamic command dispatcher. The first time you call cmd->func you get a + * dispatcher that loads the module, finds the real cmd, calls it, and then + * releases some internal storage. This means it's not safe to call a second + * time and we need to reload it. + */ + cmd_trust = grub_command_find ("append_add_db_cert"); + + /* The hi, signed with key 1, SHA-512. */ + DO_TEST (hi_signed, 1); + + /* The hi, signed with key 1, SHA-256. */ + DO_TEST (hi_signed_sha256, 1); + + /* The hi, key 1, SHA-512, second byte corrupted. */ + DO_TEST (hj_signed, 0); + + /* Message too short for a signature. */ + DO_TEST (short_msg, 0); + + /* Lorem ipsum. */ + DO_TEST (unsigned_msg, 0); + + /* The hi, signed with both keys, SHA-512. */ + DO_TEST (hi_double, 1); + + /* + * The hi, signed with both keys and with empty space to test we haven't + * broken support for adding more signatures after the fact. + */ + DO_TEST (hi_double_extended, 1); + + /* + * In enforcing mode, we shouldn't be able to load a certificate that isn't + * signed by an existing trusted key. + * + * However, procfs files automatically skip the verification test, so we can't + * easily test this. + */ + + /* Verify that testing with 2 trusted certs works. */ + DO_TEST (hi_signed_2nd, 0); + + err = (cmd_trust->func) (cmd_trust, 1, trust_args); + grub_test_assert (err != GRUB_ERR_NONE, "unexpectedly reloaded certificate 1: %d: %s", + grub_errno, grub_errmsg); + + err = (cmd_trust->func) (cmd_trust, 1, trust_args2); + grub_test_assert (err == GRUB_ERR_NONE, "loading certificate 2 failed: %d: %s", + grub_errno, grub_errmsg); + if (err != GRUB_ERR_NONE) + return; + + DO_TEST (hi_signed_2nd, 1); + DO_TEST (hi_signed, 1); + DO_TEST (hi_double, 1); + DO_TEST (hi_double_extended, 1); + + /* + * Check certificate removal. They're added to the _top_ of the db list and + * removed by position in the list. Current the list looks like [#2, #1]. + */ + cmd_distrust = grub_command_find ("append_add_dbx_cert"); + if (cmd_distrust == NULL) + { + grub_test_assert (0, "can't find command `%s'", "append_add_dbx_cert"); + return; + } + + /* Remove the certificate #1. */ + err = (cmd_distrust->func) (cmd_distrust, 1, trust_args); + grub_test_assert (err == GRUB_ERR_NONE, "distrusting certificate 1 failed: %d: %s", + grub_errno, grub_errmsg); + DO_TEST (hi_signed_2nd, 1); + DO_TEST (hi_signed, 0); + DO_TEST (hi_double, 1); + + /* Now reload certificate #1. */ + err = (cmd_trust->func) (cmd_trust, 1, trust_args); + grub_test_assert (err == GRUB_ERR_NONE, "reloading certificate 1 failed: %d: %s", + grub_errno, grub_errmsg); + DO_TEST (hi_signed_2nd, 1); + DO_TEST (hi_signed, 1); + DO_TEST (hi_double, 1); + + /* Remove the certificate #2. */ + err = (cmd_distrust->func) (cmd_distrust, 1, trust_args2); + grub_test_assert (err == GRUB_ERR_NONE, "distrusting certificate 2 failed: %d: %s", + grub_errno, grub_errmsg); + DO_TEST (hi_signed_2nd, 0); + DO_TEST (hi_signed, 1); + DO_TEST (hi_double, 1); + + /* Now reload certificate #2. */ + err = (cmd_trust->func) (cmd_trust, 1, trust_args2); + grub_test_assert (err == GRUB_ERR_NONE, "reloading certificate 2 failed: %d: %s", + grub_errno, grub_errmsg); + DO_TEST (hi_signed_2nd, 1); + DO_TEST (hi_signed, 1); + DO_TEST (hi_double, 1); + + /* Remove the certificate #1. */ + err = (cmd_distrust->func) (cmd_distrust, 1, trust_args); + grub_test_assert (err == GRUB_ERR_NONE, "distrusting certificate 1 failed: %d: %s", + grub_errno, grub_errmsg); + + /* Remove the certificate #2. */ + err = (cmd_distrust->func) (cmd_distrust, 1, trust_args2); + grub_test_assert (err == GRUB_ERR_NONE, "distrusting certificate 2 failed: %d: %s", + grub_errno, grub_errmsg); + + /* Set appended signature key managment to dynamic. */ + err = grub_env_set ("appendedsig_key_mgmt", "dynamic"); + grub_test_assert (err == GRUB_ERR_NONE, "set of key management is failed: %d: %s", + grub_errno, grub_errmsg); + + /* Get appended signatures key management. */ + key_mgmt = grub_env_get ("appendedsig_key_mgmt"); + grub_test_assert (grub_strncmp (key_mgmt, "dynamic", grub_strlen(key_mgmt)) == 0, + "getting unexpected key management: %d: %s", + grub_errno, grub_errmsg); + + cmd_trust = grub_command_find ("append_add_db_cert"); + err = (cmd_trust->func) (cmd_trust, 1, trust_args); + grub_test_assert ((err == GRUB_ERR_NONE || err == GRUB_ERR_EXISTS || GRUB_ERR_ACCESS_DENIED), + "loading certificate 1 failed: %d: %s", + grub_errno, grub_errmsg); + if (err != GRUB_ERR_NONE) + return; + + DO_TEST (hi_signed, 1); + DO_TEST (hi_double, 1); + DO_TEST (hi_double_extended, 1); + + err = (cmd_trust->func) (cmd_trust, 1, trust_args2); + grub_test_assert ((err == GRUB_ERR_NONE || err == GRUB_ERR_EXISTS || GRUB_ERR_ACCESS_DENIED), + "loading certificate 2 failed: %d: %s", + grub_errno, grub_errmsg); + if (err != GRUB_ERR_NONE) + return; + + DO_TEST (hi_signed_2nd, 1); + DO_TEST (hi_signed, 1); + DO_TEST (hi_double, 1); + DO_TEST (hi_double_extended, 1); + + cmd_distrust = grub_command_find ("append_add_dbx_cert"); + if (cmd_distrust == NULL) + { + grub_test_assert (0, "can't find command `%s'", "append_add_dbx_cert"); + return; + } + + /* Now remove certificate #1. */ + err = (cmd_distrust->func) (cmd_distrust, 1, trust_args); + grub_test_assert ((err == GRUB_ERR_NONE || err == GRUB_ERR_EXISTS), + "distrusting certificate 1 failed: %d: %s", + grub_errno, grub_errmsg); + DO_TEST (hi_signed_2nd, 1); + DO_TEST (hi_signed, 0); + DO_TEST (hi_double, 1); + + /* Now reload certificate #1. */ + err = (cmd_trust->func) (cmd_trust, 1, trust_args); + grub_test_assert (err != GRUB_ERR_NONE, "unexpectedly reloaded certificate 1: %d: %s", + grub_errno, grub_errmsg); + DO_TEST (hi_signed_2nd, 1); + DO_TEST (hi_signed, 0); + DO_TEST (hi_double, 1); + + /* Remove the certificate #2. */ + err = (cmd_distrust->func) (cmd_distrust, 1, trust_args2); + grub_test_assert ((err == GRUB_ERR_NONE || err == GRUB_ERR_EXISTS), + "distrusting certificate 2 failed: %d: %s", + grub_errno, grub_errmsg); + DO_TEST (hi_signed_2nd, 0); + DO_TEST (hi_signed, 0); + DO_TEST (hi_double, 0); + + /* Now reload certificate #2. */ + err = (cmd_trust->func) (cmd_trust, 1, trust_args2); + grub_test_assert (err != GRUB_ERR_NONE, "unexpectedly reloaded certificate 2: %d: %s", + grub_errno, grub_errmsg); + DO_TEST (hi_signed_2nd, 0); + DO_TEST (hi_signed, 0); + DO_TEST (hi_double, 0); + + /* + * Lastly, check a certificate that uses printableString rather than utf8String + * loads properly, and that a certificate with an appropriate extended key usage + * loads. + */ + err = (cmd_trust->func) (cmd_trust, 1, trust_args_printable); + grub_test_assert (err == GRUB_ERR_NONE, "trusting printable certificate failed: %d: %s", + grub_errno, grub_errmsg); + + err = (cmd_trust->func) (cmd_trust, 1, trust_args_eku); + grub_test_assert (err == GRUB_ERR_NONE, "trusting certificate with extended key usage failed: %d: %s", + grub_errno, grub_errmsg); + + grub_procfs_unregister (&certificate_der_entry); + grub_procfs_unregister (&certificate2_der_entry); + grub_procfs_unregister (&certificate_printable_der_entry); + grub_procfs_unregister (&certificate_eku_der_entry); +} + +GRUB_FUNCTIONAL_TEST (appended_signature_test, appended_signature_test); diff --git a/grub-core/tests/appended_signatures.h b/grub-core/tests/appended_signatures.h new file mode 100644 index 000000000..c6aa12d86 --- /dev/null +++ b/grub-core/tests/appended_signatures.h @@ -0,0 +1,975 @@ +unsigned char certificate_der[] = { + 0x30, 0x82, 0x05, 0x5d, 0x30, 0x82, 0x03, 0x45, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, + 0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x30, 0x3d, 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0c, 0x32, 0x47, 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, + 0x6e, 0x64, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x31, 0x30, + 0x36, 0x32, 0x39, 0x30, 0x38, 0x33, 0x36, 0x31, 0x33, 0x5a, 0x18, 0x0f, + 0x32, 0x31, 0x32, 0x31, 0x30, 0x36, 0x30, 0x35, 0x30, 0x38, 0x33, 0x36, + 0x31, 0x33, 0x5a, 0x30, 0x33, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0c, 0x28, 0x47, 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, + 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x69, 0x67, + 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x65, 0x79, 0x30, 0x82, 0x02, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, + 0x02, 0x82, 0x02, 0x01, 0x00, 0xb9, 0x09, 0xb2, 0xf6, 0x24, 0x34, 0xdc, + 0x62, 0xe6, 0x4e, 0xee, 0x04, 0xdb, 0x29, 0xdc, 0x94, 0xcc, 0xee, 0x8a, + 0x5b, 0xc3, 0x9e, 0x06, 0xba, 0xa7, 0x9b, 0xa4, 0x5f, 0x15, 0x59, 0x8e, + 0xb8, 0x6e, 0x3c, 0xeb, 0x2e, 0xf2, 0xac, 0x21, 0x42, 0xbd, 0x30, 0xa1, + 0x39, 0xe5, 0xb9, 0x4f, 0xa0, 0x53, 0xd5, 0x42, 0xdc, 0x8a, 0x87, 0x30, + 0x38, 0x93, 0x44, 0x80, 0x3b, 0x1a, 0x7e, 0x9e, 0x8e, 0x3e, 0xea, 0x45, + 0xa0, 0x11, 0x8b, 0xfb, 0x78, 0xe4, 0xbc, 0x65, 0x6b, 0x73, 0xea, 0x6e, + 0xdf, 0x7c, 0x5b, 0x63, 0x7e, 0x5b, 0x0a, 0x1c, 0xe6, 0x76, 0x19, 0xb5, + 0x01, 0xde, 0xf6, 0x65, 0x51, 0x30, 0x0a, 0x56, 0x69, 0x69, 0xe8, 0x20, + 0xf9, 0x13, 0xf1, 0xbf, 0x6f, 0xdd, 0xce, 0x94, 0x96, 0x6e, 0x63, 0xd6, + 0xfa, 0xa4, 0x91, 0x5f, 0xb3, 0x9c, 0xc7, 0xfa, 0xa9, 0xff, 0x66, 0x5f, + 0xf3, 0xab, 0x5e, 0xdf, 0x4e, 0xca, 0x11, 0xcf, 0xbf, 0xf8, 0xad, 0x65, + 0xb1, 0x49, 0x8b, 0xe9, 0x2a, 0xad, 0x7d, 0xf3, 0x0b, 0xfa, 0x5b, 0x6a, + 0x6a, 0x20, 0x12, 0x77, 0xef, 0x4b, 0xb6, 0xbe, 0x92, 0xba, 0x14, 0x9c, + 0x5e, 0xea, 0xdc, 0x56, 0x6d, 0x92, 0xd3, 0x64, 0x22, 0xf6, 0x12, 0xe8, + 0x7d, 0x5e, 0x9c, 0xd6, 0xf9, 0x75, 0x68, 0x7f, 0x8f, 0xd3, 0x6e, 0x05, + 0x94, 0x91, 0x4f, 0xa1, 0xd6, 0x50, 0x72, 0x3b, 0x11, 0x1f, 0x28, 0x13, + 0xe8, 0x25, 0x6b, 0xdf, 0xff, 0x72, 0x46, 0x25, 0xe9, 0x05, 0x6f, 0x02, + 0xc7, 0x1e, 0xc9, 0xcf, 0x99, 0xe9, 0xa7, 0xe2, 0xae, 0xbc, 0xc1, 0x22, + 0x32, 0x73, 0x2d, 0xa3, 0x70, 0x8f, 0xa7, 0x8d, 0xbf, 0x5f, 0x74, 0x05, + 0x1b, 0x5e, 0xfe, 0x97, 0x3c, 0xe7, 0x3b, 0x86, 0x0d, 0xf6, 0x38, 0xdb, + 0xd2, 0x39, 0x47, 0x82, 0x00, 0x44, 0x6c, 0x7b, 0x40, 0x24, 0x0b, 0x3a, + 0xd4, 0x19, 0x31, 0xba, 0x4e, 0x8e, 0xa3, 0x33, 0xa6, 0x78, 0xef, 0x72, + 0x9f, 0x06, 0x37, 0x01, 0x9b, 0x79, 0x0d, 0x04, 0xbf, 0xba, 0xd5, 0x1f, + 0x27, 0xdc, 0x85, 0xbb, 0xef, 0xd2, 0x60, 0xda, 0xa0, 0x3f, 0x66, 0xce, + 0x9f, 0xa2, 0x7e, 0xa8, 0x8d, 0xee, 0x14, 0x4b, 0xcb, 0x93, 0xf1, 0x38, + 0xac, 0x4f, 0xd8, 0x29, 0xf3, 0x6f, 0xd4, 0xfd, 0x4d, 0x34, 0x77, 0x58, + 0x99, 0xdb, 0x16, 0xc1, 0xd0, 0xc7, 0x43, 0x41, 0x70, 0xc4, 0xad, 0x01, + 0x29, 0x65, 0x22, 0x43, 0x00, 0x6f, 0xb3, 0x00, 0x27, 0x38, 0xc1, 0x4f, + 0xda, 0x28, 0x96, 0x42, 0xdc, 0xbc, 0x3e, 0x34, 0x8e, 0x14, 0xb8, 0xf3, + 0x86, 0x4a, 0xea, 0x16, 0x90, 0xf9, 0x0e, 0x9e, 0x8f, 0x66, 0x0c, 0xbf, + 0x29, 0xd3, 0x8f, 0xfc, 0x4d, 0x38, 0x68, 0xe2, 0xe7, 0x64, 0x32, 0x47, + 0xdd, 0x56, 0xc9, 0xe4, 0x47, 0x9f, 0x18, 0x89, 0xfc, 0x30, 0x7a, 0xae, + 0x63, 0xe4, 0xec, 0x93, 0x04, 0xd4, 0x61, 0xe7, 0xbf, 0x0a, 0x06, 0x29, + 0xc2, 0xa6, 0xd5, 0x53, 0x5d, 0x65, 0x6d, 0x4a, 0xd0, 0xb7, 0x68, 0x4d, + 0x46, 0x0a, 0xb5, 0xff, 0x52, 0x5e, 0x92, 0x7e, 0x75, 0x08, 0xa4, 0x63, + 0x0a, 0x6c, 0x31, 0x7a, 0xaa, 0x0c, 0x52, 0xf4, 0x2e, 0xcd, 0x08, 0xeb, + 0xb3, 0xbd, 0xad, 0x8b, 0x8b, 0x9b, 0x8d, 0x71, 0x42, 0x30, 0x8e, 0xc7, + 0xfd, 0xec, 0xb7, 0xe6, 0x26, 0x96, 0xf2, 0x74, 0x1b, 0x78, 0x95, 0x22, + 0x14, 0xf3, 0xc9, 0xd3, 0x79, 0x11, 0xd9, 0xb7, 0x4d, 0x0d, 0x61, 0x60, + 0x5c, 0x47, 0x50, 0xf3, 0xca, 0x84, 0x4c, 0x5c, 0x30, 0x2c, 0x6a, 0x18, + 0x26, 0xb0, 0xf3, 0xd1, 0x15, 0x19, 0x39, 0xc3, 0x23, 0x13, 0x0f, 0x9c, + 0x97, 0x2b, 0x97, 0x93, 0xf9, 0xf8, 0x18, 0x9b, 0x4a, 0x4d, 0xd6, 0xd3, + 0xf5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30, 0x0c, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, + 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x07, + 0x80, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x8f, 0xba, 0x8b, 0xf5, 0xf4, 0x77, 0xb2, 0xa4, 0x19, 0xef, 0x43, 0xb1, + 0x8b, 0x03, 0x4b, 0x45, 0x47, 0xb5, 0x2a, 0x48, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x59, 0x1c, 0xb5, + 0x52, 0x62, 0x83, 0x05, 0x3b, 0x41, 0x4c, 0x63, 0x4d, 0x5b, 0xf4, 0x8c, + 0xe6, 0xd7, 0xda, 0x87, 0x54, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, + 0x00, 0x36, 0x2d, 0x0a, 0xcb, 0x49, 0x54, 0x75, 0xd7, 0xca, 0x21, 0x86, + 0xae, 0x40, 0x0f, 0x63, 0x10, 0x35, 0xfd, 0xbc, 0xba, 0x28, 0x31, 0x33, + 0x07, 0x08, 0x64, 0x03, 0x6c, 0xd3, 0xd5, 0xf7, 0xb7, 0x79, 0x11, 0x0c, + 0xa8, 0x9e, 0xfd, 0x34, 0xa2, 0xba, 0x77, 0x15, 0x15, 0x2d, 0x2c, 0x96, + 0xae, 0x47, 0xbb, 0x82, 0x89, 0x09, 0x7f, 0xd1, 0x95, 0x69, 0x9b, 0xfe, + 0xd7, 0x6f, 0x4e, 0x68, 0xf6, 0xe7, 0x5f, 0x54, 0xa1, 0x3a, 0xeb, 0xa4, + 0xbf, 0x7a, 0xb6, 0x7f, 0xaa, 0xd8, 0xd7, 0x99, 0xcb, 0xae, 0x88, 0x6d, + 0x7a, 0xf3, 0xfa, 0x9e, 0x44, 0x2f, 0x30, 0xa8, 0xe6, 0xb9, 0x75, 0xa0, + 0x82, 0xd6, 0xb0, 0xe3, 0x03, 0xb3, 0x12, 0xa3, 0xdc, 0xb9, 0x4d, 0x93, + 0xd4, 0x30, 0xea, 0xce, 0x96, 0x92, 0x07, 0xf8, 0xba, 0xe4, 0x0f, 0x41, + 0xe3, 0x04, 0xaa, 0x8c, 0x07, 0x1a, 0x34, 0x60, 0xfc, 0xc0, 0x05, 0xd2, + 0x5a, 0xa8, 0x66, 0xef, 0xe0, 0x94, 0xc5, 0x2f, 0x0f, 0xff, 0xdc, 0x70, + 0xfb, 0xe2, 0x9d, 0x61, 0x51, 0x25, 0x02, 0xff, 0x4b, 0x69, 0xfd, 0x66, + 0xb9, 0xeb, 0x0c, 0xc8, 0x50, 0xd3, 0xb1, 0x08, 0x1e, 0x09, 0x54, 0x87, + 0xe8, 0xa3, 0x4b, 0xef, 0x0c, 0x32, 0x0a, 0x6c, 0xec, 0x27, 0x22, 0xba, + 0x7f, 0xdc, 0x52, 0x27, 0x31, 0x14, 0x9a, 0xa8, 0xf7, 0xf9, 0xeb, 0xc8, + 0xb5, 0x8d, 0x12, 0xed, 0x94, 0xab, 0x3d, 0x9a, 0xfb, 0x4e, 0x04, 0x05, + 0xd2, 0x3c, 0x7c, 0x8a, 0xed, 0x46, 0x1b, 0x7c, 0xb5, 0x6c, 0x40, 0xb8, + 0xc1, 0xbf, 0xb0, 0xd2, 0x93, 0x8e, 0xa8, 0x0f, 0xde, 0x78, 0xf3, 0x8c, + 0xd8, 0x9f, 0xf8, 0xdc, 0xa1, 0x23, 0x20, 0x40, 0x17, 0xb4, 0xdb, 0xb7, + 0x09, 0x74, 0xa7, 0x80, 0xc2, 0x12, 0xd9, 0x76, 0x79, 0x5b, 0x71, 0xa9, + 0x6c, 0xd4, 0x57, 0x48, 0xe8, 0xfe, 0xc5, 0xc2, 0x6e, 0xe7, 0x83, 0x5a, + 0x07, 0xf0, 0x33, 0xc1, 0xc1, 0x1d, 0x34, 0xd4, 0xc8, 0xb0, 0xb7, 0xdb, + 0xeb, 0xe9, 0xe3, 0x59, 0xdc, 0x7f, 0x36, 0x58, 0xa9, 0xb8, 0x52, 0xdd, + 0xf9, 0xfd, 0x1c, 0x22, 0x2f, 0x93, 0x3d, 0x53, 0x89, 0x80, 0xde, 0xa2, + 0xb5, 0xa5, 0x36, 0xbd, 0xc3, 0x92, 0x03, 0xf3, 0x93, 0xc8, 0xc7, 0x4a, + 0x0b, 0x8b, 0x62, 0xfe, 0xd0, 0xf8, 0x0d, 0x7a, 0x32, 0xb4, 0x39, 0x1a, + 0xb7, 0x4e, 0xaa, 0xc4, 0x33, 0x32, 0x90, 0x8c, 0xab, 0xd4, 0xae, 0xa5, + 0xa4, 0x85, 0xcf, 0xba, 0xe1, 0x1b, 0x26, 0x7f, 0x74, 0x02, 0x12, 0x09, + 0x89, 0x56, 0xe4, 0xe7, 0x9d, 0x91, 0xde, 0x88, 0xe7, 0x1c, 0xed, 0x80, + 0x05, 0xa8, 0x58, 0x9a, 0x3e, 0x16, 0x97, 0xd5, 0xbc, 0x54, 0xcc, 0xf0, + 0x32, 0xf2, 0x93, 0x09, 0x94, 0x9f, 0x3c, 0xd9, 0x58, 0xca, 0x68, 0x0b, + 0xde, 0x3f, 0x73, 0x64, 0xb7, 0xf4, 0xd7, 0x5f, 0x2b, 0xe7, 0x7b, 0x06, + 0xca, 0xb1, 0x3e, 0xed, 0xd2, 0xb9, 0x29, 0xc1, 0x95, 0x87, 0xad, 0xd6, + 0x63, 0x69, 0xb8, 0x1f, 0x70, 0xdb, 0xeb, 0xc7, 0x11, 0x7d, 0xe2, 0x99, + 0x64, 0x6a, 0xf5, 0x3f, 0x30, 0x74, 0x5f, 0x2a, 0x21, 0xda, 0xef, 0x44, + 0x1d, 0xad, 0x97, 0xa1, 0xfe, 0x14, 0xa7, 0x88, 0x99, 0xd0, 0x1e, 0xb0, + 0x61, 0x88, 0x09, 0xc9, 0xfa, 0xd1, 0xb3, 0xcb, 0x1d, 0x76, 0x04, 0xbe, + 0x06, 0x44, 0xd2, 0x30, 0x5e, 0x95, 0x4b, 0x96, 0xc0, 0xd6, 0xbe, 0xd0, + 0x4d, 0xf2, 0xf4, 0x71, 0x72, 0xa9, 0xbd, 0x07, 0x4f, 0xbc, 0xb3, 0x78, + 0xb4, 0x8a, 0x44, 0xbd, 0x58, 0xd5, 0x21, 0xb6, 0x47, 0x9c, 0x88, 0x1f, + 0xbc, 0xbd, 0x54, 0xfa, 0x1d, 0x49, 0xec, 0x51, 0xd9, 0x43, 0x49, 0x9c, + 0x0c, 0xfa, 0x18, 0xdb, 0xeb, 0x05, 0x77, 0xa2, 0x9a +}; +unsigned int certificate_der_len = 1377; + +unsigned char hi_signed[] = { + 0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb4, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa5, 0x30, 0x82, + 0x02, 0xa1, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02, + 0x7e, 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d, + 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47, + 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, + 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, + 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, + 0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, + 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77, + 0x97, 0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03, + 0xdf, 0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e, + 0x77, 0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52, + 0x55, 0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01, + 0xc2, 0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75, + 0x88, 0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1, + 0xd1, 0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d, + 0xe3, 0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e, + 0x45, 0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f, + 0x86, 0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae, + 0xcd, 0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5, + 0x27, 0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a, + 0xf1, 0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33, + 0x1a, 0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2, + 0x8d, 0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9, + 0xc5, 0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70, + 0xa1, 0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67, + 0x04, 0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a, + 0x9a, 0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3, + 0x1c, 0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33, + 0xd6, 0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4, + 0xe9, 0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1, + 0x60, 0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26, + 0x83, 0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41, + 0xd7, 0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda, + 0x35, 0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3, + 0x0f, 0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98, + 0xa0, 0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5, + 0xaa, 0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e, + 0xe2, 0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe, + 0xc3, 0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c, + 0x24, 0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6, + 0xe6, 0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5, + 0x5e, 0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f, + 0x74, 0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb, + 0x3b, 0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0, + 0x8a, 0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4, + 0xdc, 0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b, + 0x83, 0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52, + 0x21, 0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f, + 0x51, 0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c, + 0x19, 0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17, + 0x35, 0x23, 0x2d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0xb8, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, + 0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a +}; +unsigned int hi_signed_len = 739; + +unsigned char hj_signed[] = { + 0x68, 0x6a, 0x0a, 0x30, 0x82, 0x02, 0xb4, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa5, 0x30, 0x82, + 0x02, 0xa1, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02, + 0x7e, 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d, + 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47, + 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, + 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, + 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, + 0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, + 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77, + 0x97, 0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03, + 0xdf, 0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e, + 0x77, 0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52, + 0x55, 0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01, + 0xc2, 0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75, + 0x88, 0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1, + 0xd1, 0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d, + 0xe3, 0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e, + 0x45, 0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f, + 0x86, 0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae, + 0xcd, 0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5, + 0x27, 0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a, + 0xf1, 0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33, + 0x1a, 0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2, + 0x8d, 0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9, + 0xc5, 0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70, + 0xa1, 0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67, + 0x04, 0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a, + 0x9a, 0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3, + 0x1c, 0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33, + 0xd6, 0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4, + 0xe9, 0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1, + 0x60, 0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26, + 0x83, 0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41, + 0xd7, 0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda, + 0x35, 0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3, + 0x0f, 0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98, + 0xa0, 0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5, + 0xaa, 0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e, + 0xe2, 0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe, + 0xc3, 0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c, + 0x24, 0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6, + 0xe6, 0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5, + 0x5e, 0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f, + 0x74, 0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb, + 0x3b, 0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0, + 0x8a, 0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4, + 0xdc, 0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b, + 0x83, 0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52, + 0x21, 0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f, + 0x51, 0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c, + 0x19, 0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17, + 0x35, 0x23, 0x2d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0xb8, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, + 0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a +}; +unsigned int hj_signed_len = 739; + +unsigned char hi_signed_sha256[] = { + 0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb4, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa5, 0x30, 0x82, + 0x02, 0xa1, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x0b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02, + 0x7e, 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d, + 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47, + 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, + 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, + 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, + 0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, + 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0x96, 0x02, 0x7b, 0xa4, 0x07, + 0xa7, 0x39, 0x8d, 0xa6, 0x0b, 0xde, 0x33, 0xdd, 0xf8, 0xec, 0x24, 0x5d, + 0x06, 0x81, 0xe7, 0x3c, 0x2d, 0x0c, 0x53, 0xfb, 0x7e, 0x5a, 0xf3, 0xee, + 0xe5, 0x4c, 0x7c, 0xf7, 0xe7, 0x8f, 0x36, 0x62, 0x35, 0xb8, 0x99, 0xc3, + 0xeb, 0x85, 0x1d, 0x2e, 0x40, 0x6e, 0x2a, 0xb4, 0x3a, 0x76, 0x48, 0x4f, + 0x8b, 0x29, 0xd4, 0x9e, 0x5c, 0xd2, 0x41, 0x4d, 0xc1, 0x72, 0x0f, 0x97, + 0xe0, 0x7d, 0x88, 0xed, 0x1a, 0xb0, 0xde, 0x1b, 0x21, 0xa6, 0x0c, 0x19, + 0xd8, 0xb0, 0x12, 0x54, 0x7b, 0xd8, 0x19, 0x03, 0xbd, 0x77, 0x83, 0x23, + 0xeb, 0xeb, 0x68, 0x0a, 0x7b, 0x3a, 0x4d, 0x25, 0x44, 0xe1, 0x64, 0x8d, + 0x43, 0x5a, 0x1c, 0x9f, 0x74, 0x79, 0x31, 0x3f, 0xc7, 0x8e, 0xae, 0xe1, + 0xf9, 0x1e, 0x54, 0x12, 0x36, 0x85, 0xf2, 0x55, 0xba, 0x42, 0x60, 0x64, + 0x25, 0x9f, 0x73, 0x62, 0x42, 0xd2, 0x1c, 0x5e, 0x39, 0x4f, 0x7d, 0x91, + 0xb8, 0xf9, 0x59, 0x3c, 0x13, 0x6b, 0x84, 0x76, 0x6d, 0x8a, 0xc3, 0xcb, + 0x2d, 0x14, 0x27, 0x16, 0xdc, 0x20, 0x2c, 0xbc, 0x6b, 0xc9, 0xda, 0x9f, + 0xef, 0xe2, 0x2d, 0xc3, 0x83, 0xd8, 0xf9, 0x94, 0x18, 0xbc, 0xfe, 0x8f, + 0xa9, 0x44, 0xad, 0xff, 0x1b, 0xcb, 0x86, 0x30, 0x96, 0xa8, 0x3c, 0x7a, + 0x4b, 0x73, 0x1b, 0xa9, 0xc3, 0x3b, 0xaa, 0xd7, 0x44, 0xa8, 0x4d, 0xd6, + 0x92, 0xb6, 0x00, 0x04, 0x09, 0x05, 0x4a, 0x95, 0x02, 0x90, 0x19, 0x8c, + 0x9a, 0xa5, 0xee, 0x58, 0x24, 0xb0, 0xca, 0x5e, 0x6f, 0x73, 0xdb, 0xf5, + 0xa1, 0xf4, 0xf0, 0xa9, 0xeb, 0xe4, 0xdc, 0x55, 0x9f, 0x8f, 0x7a, 0xd0, + 0xf7, 0xb6, 0xaa, 0xa6, 0xb5, 0xb4, 0xab, 0xb8, 0x65, 0xad, 0x12, 0x32, + 0x1c, 0xe6, 0x99, 0x71, 0x93, 0xe8, 0xb4, 0x1e, 0x21, 0x27, 0x52, 0xea, + 0x8c, 0xc8, 0x79, 0x96, 0x2e, 0x48, 0x60, 0x57, 0x1c, 0x7d, 0x8c, 0x0d, + 0x07, 0xa7, 0x12, 0x83, 0x0a, 0x76, 0x6a, 0x64, 0xed, 0xbe, 0x8d, 0xaf, + 0xdf, 0x51, 0x05, 0xdd, 0xf2, 0xd3, 0xb8, 0x93, 0xa9, 0x13, 0xa5, 0x96, + 0xe8, 0xfa, 0x82, 0x02, 0x18, 0x71, 0x7a, 0x71, 0xbb, 0x39, 0x6f, 0x85, + 0xee, 0x16, 0x82, 0x27, 0x42, 0x9f, 0x83, 0xc8, 0xab, 0x6a, 0x3b, 0x99, + 0xba, 0x38, 0x92, 0x38, 0xae, 0x59, 0xfa, 0xaa, 0x40, 0x2b, 0x52, 0x95, + 0xca, 0x5e, 0xe1, 0x9b, 0x00, 0xbd, 0xb9, 0x63, 0x25, 0x8d, 0xc7, 0x22, + 0xaf, 0xe5, 0x67, 0x76, 0x91, 0xf4, 0xda, 0xc9, 0x7e, 0x9e, 0xec, 0x9b, + 0x1f, 0x7d, 0x3b, 0xfe, 0xa1, 0x20, 0x52, 0xac, 0xd0, 0xe5, 0xa6, 0xf1, + 0xfd, 0x4c, 0x08, 0x59, 0x7d, 0x50, 0xbb, 0x0c, 0xcf, 0xd8, 0xb6, 0x0f, + 0xc7, 0x19, 0xcb, 0x7a, 0x96, 0x6f, 0x0f, 0x6c, 0x71, 0x56, 0x72, 0xd1, + 0x06, 0x29, 0x0f, 0x08, 0xa2, 0x46, 0x3e, 0x58, 0x42, 0xc4, 0x8c, 0xe0, + 0x6e, 0xe9, 0x37, 0xd5, 0x2f, 0x74, 0x36, 0x1d, 0x14, 0xcb, 0x10, 0x0e, + 0x7d, 0x67, 0xbd, 0x38, 0x0e, 0xa4, 0x27, 0x1d, 0x3c, 0x78, 0x4d, 0x0d, + 0x15, 0x42, 0x70, 0x20, 0xe0, 0x1d, 0x83, 0x6c, 0x4d, 0xf1, 0x02, 0xa1, + 0x51, 0xc4, 0xc5, 0x5d, 0x69, 0x90, 0x58, 0x82, 0x94, 0x50, 0x36, 0x22, + 0xb3, 0xa4, 0x15, 0x77, 0xdc, 0x44, 0xb0, 0x50, 0xa2, 0x3f, 0xd0, 0x0e, + 0x1b, 0xfc, 0xf4, 0x5b, 0x3b, 0x7d, 0x63, 0x94, 0x22, 0xf3, 0x87, 0x0a, + 0x41, 0x8a, 0x27, 0x48, 0xcb, 0x6c, 0xfd, 0x70, 0x66, 0x5f, 0x11, 0x6f, + 0x74, 0x2c, 0x42, 0xaf, 0x74, 0x45, 0x3f, 0x0c, 0x03, 0xc8, 0x80, 0xe2, + 0x71, 0x08, 0x93, 0xbd, 0x4d, 0x18, 0x78, 0x1e, 0x8e, 0xb9, 0x3a, 0xd6, + 0x1a, 0xde, 0xf9, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0xb8, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, + 0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a +}; +unsigned int hi_signed_sha256_len = 739; + +unsigned char short_msg[] = { + 0x68, 0x69, 0x0a +}; +unsigned int short_msg_len = 3; + +unsigned char unsigned_msg[] = { + 0x53, 0x65, 0x64, 0x20, 0x75, 0x74, 0x20, 0x70, 0x65, 0x72, 0x73, 0x70, + 0x69, 0x63, 0x69, 0x61, 0x74, 0x69, 0x73, 0x20, 0x75, 0x6e, 0x64, 0x65, + 0x20, 0x6f, 0x6d, 0x6e, 0x69, 0x73, 0x20, 0x69, 0x73, 0x74, 0x65, 0x20, + 0x6e, 0x61, 0x74, 0x75, 0x73, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, + 0x73, 0x69, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74, + 0x65, 0x6d, 0x20, 0x61, 0x63, 0x63, 0x75, 0x73, 0x61, 0x6e, 0x74, 0x69, + 0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, 0x71, 0x75, + 0x65, 0x20, 0x6c, 0x61, 0x75, 0x64, 0x61, 0x6e, 0x74, 0x69, 0x75, 0x6d, + 0x2c, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6d, 0x20, 0x72, 0x65, 0x6d, 0x20, + 0x61, 0x70, 0x65, 0x72, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x65, 0x61, 0x71, + 0x75, 0x65, 0x20, 0x69, 0x70, 0x73, 0x61, 0x20, 0x71, 0x75, 0x61, 0x65, + 0x20, 0x61, 0x62, 0x20, 0x69, 0x6c, 0x6c, 0x6f, 0x20, 0x69, 0x6e, 0x76, + 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x76, 0x65, 0x72, 0x69, 0x74, + 0x61, 0x74, 0x69, 0x73, 0x20, 0x65, 0x74, 0x20, 0x71, 0x75, 0x61, 0x73, + 0x69, 0x20, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x20, 0x62, 0x65, 0x61, 0x74, 0x61, 0x65, 0x20, 0x76, 0x69, 0x74, 0x61, + 0x65, 0x20, 0x64, 0x69, 0x63, 0x74, 0x61, 0x20, 0x73, 0x75, 0x6e, 0x74, + 0x20, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x62, 0x6f, 0x2e, 0x20, + 0x4e, 0x65, 0x6d, 0x6f, 0x20, 0x65, 0x6e, 0x69, 0x6d, 0x20, 0x69, 0x70, + 0x73, 0x61, 0x6d, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74, + 0x65, 0x6d, 0x20, 0x71, 0x75, 0x69, 0x61, 0x20, 0x76, 0x6f, 0x6c, 0x75, + 0x70, 0x74, 0x61, 0x73, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x73, 0x70, + 0x65, 0x72, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x20, 0x61, 0x75, 0x74, 0x20, + 0x6f, 0x64, 0x69, 0x74, 0x20, 0x61, 0x75, 0x74, 0x20, 0x66, 0x75, 0x67, + 0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64, 0x20, 0x71, 0x75, 0x69, 0x61, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x75, 0x6e, 0x74, 0x75, + 0x72, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, + 0x72, 0x65, 0x73, 0x20, 0x65, 0x6f, 0x73, 0x20, 0x71, 0x75, 0x69, 0x20, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x20, 0x76, 0x6f, 0x6c, 0x75, + 0x70, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x20, 0x73, 0x65, 0x71, 0x75, 0x69, + 0x20, 0x6e, 0x65, 0x73, 0x63, 0x69, 0x75, 0x6e, 0x74, 0x2e, 0x20, 0x4e, + 0x65, 0x71, 0x75, 0x65, 0x20, 0x70, 0x6f, 0x72, 0x72, 0x6f, 0x20, 0x71, + 0x75, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x65, 0x73, 0x74, 0x2c, + 0x20, 0x71, 0x75, 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, + 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x71, 0x75, 0x69, 0x61, 0x20, + 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, + 0x65, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65, + 0x74, 0x75, 0x72, 0x2c, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, 0x73, 0x63, + 0x69, 0x20, 0x76, 0x65, 0x6c, 0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64, + 0x20, 0x71, 0x75, 0x69, 0x61, 0x20, 0x6e, 0x6f, 0x6e, 0x20, 0x6e, 0x75, + 0x6d, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x65, 0x69, 0x75, 0x73, 0x20, 0x6d, + 0x6f, 0x64, 0x69, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x20, + 0x69, 0x6e, 0x63, 0x69, 0x64, 0x75, 0x6e, 0x74, 0x20, 0x75, 0x74, 0x20, + 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x65, 0x20, 0x65, 0x74, 0x20, 0x64, 0x6f, + 0x6c, 0x6f, 0x72, 0x65, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x61, 0x6d, 0x20, + 0x61, 0x6c, 0x69, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x71, 0x75, 0x61, 0x65, + 0x72, 0x61, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74, + 0x65, 0x6d, 0x2e, 0x20, 0x55, 0x74, 0x20, 0x65, 0x6e, 0x69, 0x6d, 0x20, + 0x61, 0x64, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x61, 0x20, 0x76, 0x65, + 0x6e, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x71, 0x75, 0x69, 0x73, 0x20, 0x6e, + 0x6f, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x20, 0x65, 0x78, 0x65, 0x72, 0x63, + 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x6d, 0x20, 0x75, 0x6c, + 0x6c, 0x61, 0x6d, 0x20, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x69, 0x73, + 0x20, 0x73, 0x75, 0x73, 0x63, 0x69, 0x70, 0x69, 0x74, 0x20, 0x6c, 0x61, + 0x62, 0x6f, 0x72, 0x69, 0x6f, 0x73, 0x61, 0x6d, 0x2c, 0x20, 0x6e, 0x69, + 0x73, 0x69, 0x20, 0x75, 0x74, 0x20, 0x61, 0x6c, 0x69, 0x71, 0x75, 0x69, + 0x64, 0x20, 0x65, 0x78, 0x20, 0x65, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x64, 0x69, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x61, + 0x74, 0x75, 0x72, 0x3f, 0x20, 0x51, 0x75, 0x69, 0x73, 0x20, 0x61, 0x75, + 0x74, 0x65, 0x6d, 0x20, 0x76, 0x65, 0x6c, 0x20, 0x65, 0x75, 0x6d, 0x20, + 0x69, 0x75, 0x72, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x68, 0x65, + 0x6e, 0x64, 0x65, 0x72, 0x69, 0x74, 0x20, 0x71, 0x75, 0x69, 0x20, 0x69, + 0x6e, 0x20, 0x65, 0x61, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, + 0x74, 0x65, 0x20, 0x76, 0x65, 0x6c, 0x69, 0x74, 0x20, 0x65, 0x73, 0x73, + 0x65, 0x20, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x6e, 0x69, 0x68, 0x69, 0x6c, + 0x20, 0x6d, 0x6f, 0x6c, 0x65, 0x73, 0x74, 0x69, 0x61, 0x65, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x61, 0x74, 0x75, 0x72, 0x2c, 0x20, + 0x76, 0x65, 0x6c, 0x20, 0x69, 0x6c, 0x6c, 0x75, 0x6d, 0x20, 0x71, 0x75, + 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x65, 0x75, + 0x6d, 0x20, 0x66, 0x75, 0x67, 0x69, 0x61, 0x74, 0x20, 0x71, 0x75, 0x6f, + 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x73, 0x20, 0x6e, 0x75, + 0x6c, 0x6c, 0x61, 0x20, 0x70, 0x61, 0x72, 0x69, 0x61, 0x74, 0x75, 0x72, + 0x3f, 0x0a +}; +unsigned int unsigned_msg_len = 866; + +unsigned char certificate2_der[] = { + 0x30, 0x82, 0x05, 0x52, 0x30, 0x82, 0x03, 0x3a, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, + 0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x30, 0x3a, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0c, 0x2f, 0x47, 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, + 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x74, 0x79, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x37, 0x32, 0x38, + 0x31, 0x33, 0x32, 0x34, 0x32, 0x39, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x32, + 0x30, 0x30, 0x37, 0x30, 0x34, 0x31, 0x33, 0x32, 0x34, 0x32, 0x39, 0x5a, + 0x30, 0x2b, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x20, 0x47, 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, + 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x65, 0x79, 0x30, 0x82, 0x02, + 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, + 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xb0, 0x2f, 0x50, 0x01, 0x9c, 0x0e, + 0xd6, 0x8c, 0x07, 0xca, 0xc1, 0xcf, 0xbc, 0x03, 0xdd, 0xd3, 0xfa, 0xe3, + 0x4f, 0x71, 0xc1, 0x30, 0xaa, 0x09, 0x96, 0xe4, 0xd0, 0x6c, 0x42, 0x93, + 0xdb, 0x35, 0xf6, 0x7e, 0x1b, 0x67, 0xc0, 0xc2, 0x2d, 0x5b, 0xec, 0xca, + 0x35, 0x06, 0x32, 0x6c, 0x7b, 0x2c, 0xd3, 0x71, 0x2b, 0xe9, 0x7a, 0x19, + 0xd1, 0xf2, 0xa0, 0x7f, 0xd7, 0x4d, 0x6e, 0x28, 0xbb, 0xae, 0x49, 0x4a, + 0xbc, 0xea, 0x47, 0x67, 0xb8, 0x36, 0xa6, 0xf5, 0x0d, 0x0e, 0x20, 0x14, + 0x0c, 0x66, 0x67, 0x28, 0xb5, 0x97, 0x8b, 0x1f, 0x5e, 0x32, 0x06, 0x29, + 0x9c, 0x99, 0x92, 0x0f, 0x73, 0xac, 0xfd, 0xd2, 0x1d, 0xf2, 0xa8, 0x55, + 0x9d, 0x1b, 0xd8, 0x3d, 0xb0, 0x76, 0x9a, 0xb6, 0x6c, 0x9f, 0x62, 0x37, + 0x2f, 0xc0, 0xef, 0x44, 0xb3, 0x0d, 0x4a, 0x3e, 0x4f, 0x7d, 0xbd, 0xdb, + 0xd8, 0x75, 0x5f, 0x68, 0xe3, 0xf0, 0xec, 0x82, 0x66, 0x7c, 0x31, 0x70, + 0xa9, 0xa1, 0x6f, 0x38, 0x9f, 0xdf, 0xf5, 0xf0, 0x7d, 0x23, 0x9d, 0x34, + 0xa5, 0x85, 0xd3, 0xdf, 0x68, 0x41, 0xfc, 0x4f, 0x89, 0x45, 0x3c, 0x24, + 0x81, 0xa6, 0xf2, 0x3c, 0x02, 0x26, 0x09, 0x48, 0xdd, 0xfe, 0x4b, 0xb6, + 0x66, 0xbf, 0x8f, 0xe5, 0x5f, 0xf0, 0x5d, 0x8a, 0x61, 0x2e, 0x5f, 0x9f, + 0x80, 0xd9, 0xd5, 0xe6, 0x41, 0xd8, 0x10, 0x5e, 0x7a, 0xc6, 0xdb, 0x89, + 0xc7, 0xca, 0x6c, 0x5b, 0xb1, 0x4e, 0x7d, 0x0c, 0x03, 0xfd, 0x50, 0xca, + 0xbf, 0xbb, 0xe2, 0x69, 0x4b, 0x4e, 0xc2, 0x3d, 0x75, 0xfa, 0xd1, 0xcc, + 0xd6, 0xf9, 0x39, 0xb9, 0xdc, 0x53, 0xad, 0x62, 0xfb, 0x1b, 0x94, 0x26, + 0x7f, 0x21, 0x54, 0x5c, 0xb7, 0xdc, 0xe7, 0x96, 0x8c, 0xce, 0x75, 0xe0, + 0x17, 0x01, 0x3a, 0x3c, 0x77, 0x6e, 0xa4, 0x8b, 0x7a, 0x83, 0x28, 0x7a, + 0xf7, 0xb0, 0x5f, 0xfc, 0x7f, 0x2d, 0x2e, 0xec, 0xf5, 0xeb, 0x9c, 0x63, + 0x74, 0xd0, 0xe5, 0xdc, 0x19, 0xe4, 0x71, 0xc5, 0x4a, 0x8a, 0x54, 0xa4, + 0xe0, 0x7d, 0x4e, 0xbf, 0x53, 0x30, 0xaf, 0xd0, 0xeb, 0x96, 0xc3, 0xbb, + 0x65, 0xf7, 0x67, 0xf5, 0xae, 0xd3, 0x96, 0xf2, 0x63, 0xc8, 0x69, 0xf7, + 0x47, 0xcb, 0x27, 0x79, 0xe1, 0xff, 0x2f, 0x68, 0xdf, 0x1e, 0xb3, 0xb8, + 0x0c, 0xc5, 0x58, 0x73, 0xcc, 0xfe, 0x8c, 0xda, 0x4e, 0x3b, 0x01, 0x04, + 0xcd, 0xcb, 0xb8, 0x3e, 0x06, 0xfd, 0x4c, 0x0a, 0x9f, 0x5e, 0x76, 0x8c, + 0x0c, 0x83, 0x75, 0x09, 0x08, 0xb2, 0xdb, 0xf4, 0x49, 0x4e, 0xa0, 0xf2, + 0x0c, 0x7b, 0x87, 0x38, 0x9e, 0x22, 0x67, 0xbd, 0xd1, 0x97, 0x57, 0x24, + 0xf1, 0x46, 0x07, 0xf9, 0xd2, 0x1b, 0xec, 0x25, 0x5e, 0x67, 0xd9, 0x66, + 0x23, 0x1b, 0xd3, 0xe4, 0xaa, 0xec, 0x88, 0xf0, 0x7e, 0x15, 0x83, 0x51, + 0x31, 0x67, 0x51, 0x76, 0x5f, 0x55, 0xd7, 0x36, 0xdf, 0x4a, 0x84, 0x0b, + 0x6f, 0x5c, 0xbb, 0x5b, 0x8f, 0x37, 0x23, 0x7f, 0xf8, 0x17, 0x84, 0xa2, + 0x70, 0x20, 0x07, 0x0c, 0x90, 0x3a, 0x04, 0xfd, 0xf0, 0x08, 0x4a, 0xb1, + 0x16, 0x0f, 0xe6, 0xf6, 0x40, 0x51, 0x83, 0xd2, 0x87, 0x40, 0x9c, 0x1c, + 0x9f, 0x13, 0x38, 0x17, 0xd3, 0x34, 0x58, 0xad, 0x05, 0x71, 0xa0, 0x73, + 0xca, 0x40, 0xa6, 0xa4, 0x81, 0x02, 0xee, 0xa8, 0x72, 0x41, 0xa1, 0x41, + 0x18, 0x64, 0x8a, 0x86, 0x8a, 0x5d, 0xe6, 0x4f, 0x0a, 0xc5, 0x95, 0x98, + 0xf9, 0x78, 0xfe, 0x19, 0x0d, 0xc9, 0xb3, 0x89, 0xc1, 0x2b, 0x09, 0xbe, + 0xf1, 0xd2, 0x04, 0x5d, 0xcc, 0x28, 0xf5, 0x4b, 0xd2, 0x20, 0x4f, 0xc5, + 0x41, 0x9d, 0x8c, 0x85, 0xd8, 0xb0, 0x68, 0x5e, 0xc1, 0x0c, 0xb7, 0x24, + 0x4d, 0x67, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, + 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, + 0x07, 0x80, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0xac, 0xf5, 0x47, 0x17, 0xd9, 0x7d, 0xc1, 0xb1, 0xc4, 0x41, 0xe1, + 0x41, 0x60, 0xcb, 0x37, 0x11, 0x60, 0x28, 0x78, 0x5f, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x21, 0x94, + 0xfb, 0xf9, 0xb2, 0x43, 0xe9, 0x33, 0xd7, 0x50, 0x7d, 0xc7, 0x37, 0xdb, + 0xd5, 0x82, 0x5a, 0x4e, 0xbe, 0x1b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, + 0x01, 0x00, 0x96, 0x70, 0x65, 0x26, 0x42, 0xf8, 0xdc, 0x69, 0xde, 0xcf, + 0x41, 0x3a, 0x2e, 0x7f, 0x5b, 0xf1, 0xf9, 0x3b, 0x9b, 0xd2, 0x4e, 0x64, + 0x48, 0x81, 0xe4, 0x5d, 0x1e, 0x22, 0xce, 0x68, 0x63, 0x62, 0xe5, 0x1b, + 0x9b, 0xf2, 0xc7, 0x12, 0xda, 0x1e, 0x9b, 0x90, 0x84, 0x79, 0x48, 0x12, + 0xe6, 0x21, 0x6f, 0x2f, 0x7e, 0x18, 0x77, 0xdb, 0x8c, 0xc4, 0xd1, 0x0d, + 0x91, 0xbf, 0x39, 0x22, 0x0f, 0x64, 0xcf, 0x25, 0x2e, 0x8c, 0x1f, 0x91, + 0x81, 0xb5, 0xe9, 0x6c, 0x02, 0x3a, 0xf8, 0x07, 0xa2, 0x6f, 0x46, 0x5d, + 0x7b, 0xfd, 0x43, 0xff, 0x41, 0x0f, 0xe2, 0x57, 0x1c, 0xbd, 0x48, 0x60, + 0x53, 0x11, 0x48, 0x87, 0x88, 0x9d, 0x13, 0x82, 0x40, 0x68, 0x44, 0x2c, + 0xc6, 0xc8, 0x95, 0x27, 0x4f, 0xb6, 0xb9, 0x4a, 0x22, 0x0a, 0xfd, 0xe4, + 0x46, 0x8f, 0x35, 0x12, 0x98, 0x5a, 0x34, 0x6f, 0x2b, 0x57, 0x62, 0xa1, + 0x4d, 0x8d, 0x79, 0x37, 0xe4, 0x6b, 0x8a, 0x32, 0x5b, 0xcb, 0xef, 0x79, + 0x11, 0xed, 0xa7, 0xf8, 0x7a, 0x1c, 0xbd, 0x86, 0xdc, 0x0e, 0x2e, 0xfd, + 0xd3, 0x51, 0xbb, 0x73, 0xad, 0x00, 0xa0, 0x1b, 0xf9, 0x1d, 0xd1, 0x4a, + 0xe4, 0xd4, 0x02, 0x63, 0x2b, 0x39, 0x5f, 0x18, 0x08, 0x2f, 0x42, 0xb7, + 0x23, 0x4b, 0x48, 0x46, 0x1f, 0x63, 0x87, 0xae, 0x6d, 0xd5, 0xdb, 0x60, + 0xf8, 0x5f, 0xd3, 0x13, 0xec, 0xca, 0xdd, 0x60, 0x60, 0x79, 0x52, 0x70, + 0x47, 0xae, 0x1d, 0x38, 0x78, 0x71, 0xcf, 0xb3, 0x04, 0x03, 0xbe, 0xba, + 0x81, 0xba, 0x74, 0xb1, 0x30, 0x35, 0xdc, 0xea, 0x21, 0x4a, 0x9b, 0x70, + 0xfb, 0xd6, 0x60, 0x59, 0x78, 0x0c, 0x4d, 0x39, 0x19, 0x1d, 0xe5, 0x75, + 0xba, 0x07, 0xf4, 0x22, 0x37, 0x64, 0xb7, 0xf2, 0x9a, 0xc9, 0x11, 0x2d, + 0x8e, 0x58, 0xa6, 0xcf, 0x83, 0xf1, 0xcb, 0x6c, 0x7f, 0x02, 0xbd, 0xda, + 0x03, 0x92, 0xa9, 0x45, 0x24, 0x56, 0xc5, 0xbd, 0x41, 0xd1, 0x20, 0x86, + 0xc0, 0xb6, 0xb7, 0xe8, 0xa7, 0xb2, 0x46, 0xf7, 0x8e, 0xa9, 0x38, 0x0e, + 0x23, 0x77, 0x3c, 0x0d, 0x66, 0x83, 0x6a, 0x1a, 0x6b, 0x7f, 0x54, 0x11, + 0x58, 0x0d, 0x4a, 0xb5, 0x74, 0x60, 0xca, 0xed, 0xff, 0x91, 0x47, 0xd9, + 0x29, 0xe0, 0xaa, 0x8c, 0xa8, 0x8f, 0x10, 0x4c, 0x15, 0x7d, 0xce, 0x95, + 0xf9, 0x87, 0x1e, 0x18, 0x38, 0x18, 0xfc, 0xcc, 0xaf, 0x91, 0x17, 0x3f, + 0xfa, 0xf0, 0x8a, 0x09, 0x6f, 0xba, 0x4e, 0x53, 0xf7, 0xfa, 0x4f, 0x20, + 0xa3, 0xf4, 0x4a, 0x5a, 0xde, 0x17, 0x1c, 0x29, 0x6a, 0x6f, 0x03, 0x48, + 0xdf, 0xad, 0x4f, 0xe4, 0xbc, 0x71, 0xc4, 0x72, 0x32, 0x11, 0x84, 0xac, + 0x09, 0xd2, 0x18, 0x44, 0x35, 0xf1, 0xcd, 0xaf, 0xa8, 0x98, 0xe0, 0x8b, + 0xec, 0xa0, 0x83, 0x37, 0xc3, 0x35, 0x85, 0xd6, 0xd8, 0x1b, 0xe0, 0x75, + 0xdc, 0xfd, 0xde, 0xc9, 0xeb, 0xd5, 0x18, 0x0f, 0xd3, 0x4c, 0x2f, 0x71, + 0xdc, 0x48, 0xe3, 0x14, 0xeb, 0xda, 0x00, 0x24, 0x24, 0x9e, 0xa3, 0x8e, + 0x3e, 0x08, 0x6f, 0x22, 0x24, 0xd6, 0xc4, 0x85, 0x8f, 0x68, 0x00, 0x4a, + 0x82, 0x4c, 0x33, 0x6e, 0xa5, 0x35, 0x7b, 0xeb, 0x4b, 0xdc, 0xa0, 0xa6, + 0x65, 0x6f, 0x5a, 0x7a, 0xdf, 0x8a, 0x01, 0x52, 0xa1, 0x6c, 0xff, 0x59, + 0x22, 0x7f, 0xe1, 0x96, 0x1b, 0x19, 0xb8, 0xf9, 0x5d, 0x44, 0x9f, 0x91, + 0x03, 0x3c, 0x3d, 0xa1, 0x2a, 0xb6, 0x5a, 0x51, 0xa0, 0xce, 0x4a, 0x88, + 0x22, 0x72, 0x9c, 0xdc, 0xc0, 0x47, 0x76, 0x35, 0x84, 0x75, 0x9b, 0x87, + 0x5c, 0xd3, 0xcf, 0xe7, 0xdd, 0xa3, 0x57, 0x14, 0xdf, 0x00, 0xfd, 0x19, + 0x2a, 0x7d, 0x89, 0x27, 0x1c, 0x78, 0x97, 0x04, 0x58, 0x48 +}; +unsigned int certificate2_der_len = 1366; + +unsigned char hi_signed_2nd[] = { + 0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb1, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa2, 0x30, 0x82, + 0x02, 0x9e, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02, + 0x7b, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a, + 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47, + 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, + 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14, + 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07, + 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, + 0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90, + 0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99, + 0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07, + 0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe, + 0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f, + 0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f, + 0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f, + 0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53, + 0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2, + 0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0, + 0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf, + 0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05, + 0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d, + 0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d, + 0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81, + 0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36, + 0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5, + 0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc, + 0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70, + 0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94, + 0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3, + 0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20, + 0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2, + 0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac, + 0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb, + 0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49, + 0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0, + 0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb, + 0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76, + 0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5, + 0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49, + 0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b, + 0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25, + 0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e, + 0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0, + 0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98, + 0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b, + 0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83, + 0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e, + 0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a, + 0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7, + 0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26, + 0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xb5, + 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, + 0x65, 0x64, 0x7e, 0x0a +}; +unsigned int hi_signed_2nd_len = 736; + +unsigned char hi_double[] = { + 0x68, 0x69, 0x0a, 0x30, 0x82, 0x05, 0x2f, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x05, 0x20, 0x30, 0x82, + 0x05, 0x1c, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x04, + 0xf9, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a, + 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47, + 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, + 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14, + 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07, + 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, + 0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90, + 0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99, + 0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07, + 0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe, + 0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f, + 0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f, + 0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f, + 0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53, + 0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2, + 0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0, + 0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf, + 0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05, + 0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d, + 0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d, + 0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81, + 0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36, + 0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5, + 0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc, + 0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70, + 0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94, + 0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3, + 0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20, + 0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2, + 0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac, + 0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb, + 0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49, + 0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0, + 0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb, + 0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76, + 0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5, + 0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49, + 0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b, + 0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25, + 0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e, + 0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0, + 0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98, + 0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b, + 0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83, + 0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e, + 0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a, + 0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7, + 0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26, + 0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac, + 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d, 0x31, + 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47, 0x72, + 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, + 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x65, + 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, + 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, + 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, 0x0b, + 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77, 0x97, + 0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03, 0xdf, + 0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e, 0x77, + 0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52, 0x55, + 0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01, 0xc2, + 0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75, 0x88, + 0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1, 0xd1, + 0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d, 0xe3, + 0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e, 0x45, + 0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f, 0x86, + 0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae, 0xcd, + 0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5, 0x27, + 0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a, 0xf1, + 0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33, 0x1a, + 0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2, 0x8d, + 0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9, 0xc5, + 0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70, 0xa1, + 0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67, 0x04, + 0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a, 0x9a, + 0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3, 0x1c, + 0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33, 0xd6, + 0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4, 0xe9, + 0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1, 0x60, + 0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26, 0x83, + 0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41, 0xd7, + 0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda, 0x35, + 0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3, 0x0f, + 0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98, 0xa0, + 0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5, 0xaa, + 0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e, 0xe2, + 0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe, 0xc3, + 0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c, 0x24, + 0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6, 0xe6, + 0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5, 0x5e, + 0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f, 0x74, + 0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb, 0x3b, + 0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0, 0x8a, + 0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4, 0xdc, + 0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b, 0x83, + 0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52, 0x21, + 0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f, 0x51, + 0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c, 0x19, + 0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17, 0x35, + 0x23, 0x2d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x33, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, + 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a +}; +unsigned int hi_double_len = 1374; + +unsigned char hi_double_extended[] = { + 0x68, 0x69, 0x0a, 0x30, 0x82, 0x05, 0x2f, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x05, 0x20, 0x30, 0x82, + 0x05, 0x1c, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x04, + 0xf9, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a, + 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47, + 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, + 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14, + 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07, + 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, + 0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90, + 0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99, + 0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07, + 0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe, + 0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f, + 0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f, + 0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f, + 0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53, + 0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2, + 0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0, + 0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf, + 0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05, + 0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d, + 0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d, + 0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81, + 0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36, + 0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5, + 0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc, + 0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70, + 0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94, + 0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3, + 0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20, + 0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2, + 0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac, + 0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb, + 0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49, + 0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0, + 0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb, + 0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76, + 0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5, + 0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49, + 0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b, + 0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25, + 0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e, + 0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0, + 0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98, + 0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b, + 0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83, + 0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e, + 0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a, + 0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7, + 0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26, + 0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac, + 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d, 0x31, + 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47, 0x72, + 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, + 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x65, + 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, + 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, + 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, 0x0b, + 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77, 0x97, + 0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03, 0xdf, + 0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e, 0x77, + 0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52, 0x55, + 0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01, 0xc2, + 0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75, 0x88, + 0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1, 0xd1, + 0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d, 0xe3, + 0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e, 0x45, + 0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f, 0x86, + 0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae, 0xcd, + 0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5, 0x27, + 0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a, 0xf1, + 0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33, 0x1a, + 0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2, 0x8d, + 0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9, 0xc5, + 0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70, 0xa1, + 0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67, 0x04, + 0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a, 0x9a, + 0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3, 0x1c, + 0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33, 0xd6, + 0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4, 0xe9, + 0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1, 0x60, + 0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26, 0x83, + 0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41, 0xd7, + 0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda, 0x35, + 0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3, 0x0f, + 0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98, 0xa0, + 0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5, 0xaa, + 0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e, 0xe2, + 0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe, 0xc3, + 0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c, 0x24, + 0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6, 0xe6, + 0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5, 0x5e, + 0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f, 0x74, + 0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb, 0x3b, + 0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0, 0x8a, + 0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4, 0xdc, + 0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b, 0x83, + 0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52, 0x21, + 0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f, 0x51, + 0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c, 0x19, + 0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17, 0x35, + 0x23, 0x2d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x34, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, + 0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a +}; +unsigned int hi_double_extended_len = 1375; + +unsigned char certificate_printable_der[] = { + 0x30, 0x82, 0x03, 0x39, 0x30, 0x82, 0x02, 0x21, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0xde, 0xf6, 0x22, 0xc4, 0xf2, 0xf1, 0x86, 0x02, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x30, 0x2a, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x1f, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, + 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, + 0x43, 0x41, 0x20, 0x32, 0x20, 0x28, 0x62, 0x65, 0x74, 0x61, 0x29, 0x30, + 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x31, 0x30, 0x33, 0x31, 0x31, 0x34, 0x31, + 0x39, 0x32, 0x33, 0x5a, 0x17, 0x0d, 0x33, 0x37, 0x31, 0x30, 0x32, 0x35, + 0x31, 0x34, 0x31, 0x39, 0x32, 0x33, 0x5a, 0x30, 0x2f, 0x31, 0x2d, 0x30, + 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x52, 0x65, 0x64, 0x20, + 0x48, 0x61, 0x74, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, + 0x6f, 0x6f, 0x74, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, + 0x33, 0x20, 0x28, 0x62, 0x65, 0x74, 0x61, 0x29, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xbd, 0xda, 0xa1, 0xed, 0x8d, 0x8e, 0x15, + 0x5c, 0xf8, 0x01, 0x77, 0x48, 0x4a, 0x60, 0x96, 0xf9, 0x27, 0xfa, 0xe2, + 0xb1, 0x69, 0x0f, 0x51, 0x19, 0x52, 0x7e, 0xc4, 0x34, 0x8e, 0xe1, 0x9b, + 0x9c, 0xa4, 0xb1, 0x5c, 0xd6, 0x81, 0x98, 0x78, 0xfe, 0xa9, 0xe5, 0x0b, + 0x00, 0xba, 0x9c, 0x64, 0x7e, 0xc7, 0xcc, 0x72, 0xb1, 0x73, 0x4b, 0x11, + 0x07, 0x52, 0xf0, 0x20, 0x96, 0x8b, 0x99, 0x39, 0xde, 0xdb, 0xfa, 0x3d, + 0x45, 0xe2, 0x98, 0x7b, 0x0c, 0x41, 0xe4, 0x0c, 0xb5, 0x5d, 0x92, 0x74, + 0x39, 0x96, 0xe1, 0x97, 0x97, 0xa1, 0xad, 0x2e, 0xcc, 0xd0, 0x1b, 0x4d, + 0x9d, 0xbd, 0x3e, 0xa9, 0x36, 0x8e, 0xcc, 0xc7, 0x5f, 0x6a, 0x7d, 0x39, + 0x5e, 0x0b, 0x8d, 0xca, 0xe4, 0x83, 0xe9, 0x3b, 0x5c, 0x86, 0x47, 0xd4, + 0xba, 0x7d, 0x98, 0x26, 0xa1, 0xf4, 0xe8, 0x90, 0x6b, 0x0f, 0xf1, 0x6b, + 0x8c, 0xe3, 0xa2, 0x80, 0x3c, 0x96, 0xf1, 0x0a, 0xb6, 0x66, 0xc0, 0x4b, + 0x61, 0xf7, 0x74, 0xcd, 0xd3, 0x7b, 0x8e, 0x5e, 0x39, 0xda, 0x99, 0x20, + 0x33, 0x93, 0xd3, 0xf0, 0x7f, 0xad, 0x35, 0xe9, 0x88, 0x8d, 0x9c, 0xbf, + 0x65, 0xf1, 0x47, 0x02, 0xf9, 0x7c, 0xed, 0x27, 0x5f, 0x4a, 0x65, 0x3c, + 0xcf, 0x5f, 0x0e, 0x88, 0x95, 0x74, 0xde, 0xfb, 0x9e, 0x2e, 0x91, 0x9b, + 0x45, 0x37, 0xc8, 0x85, 0xff, 0xe3, 0x41, 0x70, 0xfe, 0xd5, 0xef, 0x0e, + 0x82, 0x22, 0x08, 0xb7, 0x3b, 0x44, 0x3e, 0xdc, 0x5b, 0x7f, 0xba, 0xbf, + 0xe6, 0x58, 0x9d, 0x02, 0x6e, 0x75, 0xbf, 0x50, 0xec, 0xcf, 0x3f, 0xa5, + 0x91, 0x0a, 0xe2, 0x59, 0x2c, 0xc3, 0xe7, 0x05, 0x03, 0xe8, 0xf2, 0x6f, + 0x2a, 0x04, 0x68, 0x9a, 0x31, 0x32, 0x8f, 0x04, 0x35, 0xcd, 0x1f, 0x34, + 0xcc, 0x4f, 0x79, 0x5a, 0x99, 0x8d, 0x9d, 0x5c, 0xf5, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x1d, 0x06, + 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x65, 0xc5, 0xbe, 0xca, + 0xe6, 0x59, 0x6a, 0xfd, 0x6c, 0x71, 0xc4, 0xa7, 0x98, 0xc6, 0x25, 0x8d, + 0x7b, 0x67, 0x05, 0xd0, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x18, 0x30, 0x16, 0x80, 0x14, 0x81, 0xf8, 0xee, 0x47, 0x5c, 0x3e, 0xed, + 0xfb, 0xce, 0xa5, 0x84, 0xbe, 0xd7, 0xae, 0xdb, 0xd3, 0x7d, 0x64, 0xb3, + 0x2a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x66, 0x1e, 0x3d, + 0x1d, 0x53, 0x33, 0xde, 0x4e, 0xc7, 0xc4, 0xf4, 0xdf, 0xda, 0x18, 0x19, + 0x8a, 0xa9, 0xff, 0xe2, 0x63, 0x2b, 0xbe, 0xf2, 0x61, 0x63, 0xe2, 0xf6, + 0xed, 0x47, 0x1a, 0x71, 0x02, 0xec, 0x2a, 0xef, 0x89, 0x77, 0xe3, 0xfd, + 0x86, 0x69, 0xf1, 0x3f, 0x0d, 0xf9, 0x6e, 0xf9, 0x3b, 0xad, 0x26, 0x47, + 0xb7, 0xf2, 0x0d, 0xad, 0x23, 0xa3, 0x67, 0x3b, 0xcb, 0x6d, 0x9e, 0x03, + 0x0f, 0xbc, 0x69, 0x73, 0x9f, 0xd4, 0xa5, 0x0f, 0x6f, 0xf8, 0xab, 0x4d, + 0x36, 0xd1, 0xe0, 0xe0, 0x5d, 0x20, 0x43, 0x90, 0xc4, 0x65, 0x61, 0x93, + 0xe2, 0x0f, 0x51, 0x59, 0x0a, 0xf7, 0x88, 0x70, 0x57, 0xb9, 0x04, 0xa9, + 0x32, 0x57, 0x9c, 0xb3, 0x57, 0x38, 0x8b, 0x8e, 0x46, 0xc8, 0x32, 0x6c, + 0xb4, 0xf3, 0x96, 0x7f, 0x4b, 0xf0, 0x88, 0xf9, 0x7f, 0xe2, 0x71, 0xe1, + 0x8b, 0xe2, 0x14, 0xf1, 0x4b, 0x25, 0x00, 0x48, 0x1c, 0x7e, 0xe5, 0x8d, + 0x65, 0x2d, 0xeb, 0x72, 0x4f, 0x92, 0x44, 0xf3, 0xe6, 0xe0, 0xd0, 0xdf, + 0x85, 0xa8, 0x13, 0x4a, 0xfb, 0x99, 0xca, 0x14, 0x2c, 0x97, 0x80, 0x93, + 0x27, 0xd3, 0x20, 0xf8, 0x6d, 0x29, 0x28, 0x2c, 0xb9, 0x77, 0xea, 0xb1, + 0x63, 0xbd, 0x7d, 0x53, 0xfd, 0x4a, 0x62, 0x64, 0x0b, 0x98, 0xa8, 0xae, + 0x11, 0xfc, 0x6e, 0x8d, 0x63, 0xd4, 0x15, 0x55, 0xc6, 0x4c, 0x74, 0xf5, + 0x5f, 0xa0, 0xb9, 0x2c, 0x2d, 0x9a, 0x7a, 0x87, 0x6e, 0xf0, 0x5e, 0x25, + 0xed, 0xfc, 0xd8, 0xc4, 0x34, 0x33, 0x32, 0xad, 0x01, 0xd4, 0x4b, 0x49, + 0x51, 0xc2, 0x07, 0x7f, 0x90, 0x6d, 0xea, 0xf5, 0x4c, 0x41, 0x71, 0x64, + 0xeb, 0x1f, 0x29, 0xa3, 0x1f, 0x64, 0xa2, 0x1e, 0x0e, 0x6f, 0xa1, 0x67, + 0x99, 0x8d, 0x98, 0x1c, 0xb8, 0x53, 0x9d, 0x30, 0x1d, 0xae, 0x32, 0x56, + 0xd2 +}; +unsigned int certificate_printable_der_len = 829; + +unsigned char certificate_eku_der[] = { + 0x30, 0x82, 0x03, 0x90, 0x30, 0x82, 0x02, 0x78, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0xd3, 0x9c, 0x41, 0x33, 0xdd, 0x6b, 0x5f, 0x45, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x30, 0x47, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0c, 0x18, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, + 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, + 0x43, 0x41, 0x20, 0x36, 0x31, 0x22, 0x30, 0x20, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x13, 0x73, 0x65, 0x63, + 0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65, 0x64, 0x68, 0x61, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x32, + 0x31, 0x35, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a, 0x17, 0x0d, 0x33, + 0x38, 0x30, 0x31, 0x31, 0x37, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a, + 0x30, 0x4e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x1f, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, 0x53, 0x65, 0x63, + 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, 0x53, 0x69, 0x67, + 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x36, 0x30, 0x32, 0x31, 0x22, 0x30, 0x20, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, + 0x13, 0x73, 0x65, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65, + 0x64, 0x68, 0x61, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xaa, 0x6f, 0xbb, 0x92, 0x77, 0xd7, 0x15, + 0xef, 0x88, 0x80, 0x88, 0xc0, 0xe7, 0x89, 0xeb, 0x35, 0x76, 0xf4, 0x85, + 0x05, 0x0f, 0x19, 0xe4, 0x5f, 0x25, 0xdd, 0xc1, 0xa2, 0xe5, 0x5c, 0x06, + 0xfb, 0xf1, 0x06, 0xb5, 0x65, 0x45, 0xcb, 0xbd, 0x19, 0x33, 0x54, 0xb5, + 0x1a, 0xcd, 0xe4, 0xa8, 0x35, 0x2a, 0xfe, 0x9c, 0x53, 0xf4, 0xc6, 0x76, + 0xdb, 0x1f, 0x8a, 0xd4, 0x7b, 0x18, 0x11, 0xaf, 0xa3, 0x90, 0xd4, 0xdd, + 0x4d, 0xd5, 0x42, 0xcc, 0x14, 0x9a, 0x64, 0x6b, 0xc0, 0x7f, 0xaa, 0x1c, + 0x94, 0x47, 0x4d, 0x79, 0xbd, 0x57, 0x9a, 0xbf, 0x99, 0x4e, 0x96, 0xa9, + 0x31, 0x2c, 0xa9, 0xe7, 0x14, 0x65, 0x86, 0xc8, 0xac, 0x79, 0x5e, 0x78, + 0xa4, 0x3c, 0x00, 0x24, 0xd3, 0xf7, 0xe1, 0xf5, 0x12, 0xad, 0xa0, 0x29, + 0xe5, 0xfe, 0x80, 0xae, 0xf8, 0xaa, 0x60, 0x36, 0xe7, 0xe8, 0x94, 0xcb, + 0xe9, 0xd1, 0xcc, 0x0b, 0x4d, 0xf7, 0xde, 0xeb, 0x52, 0xd2, 0x73, 0x09, + 0x28, 0xdf, 0x48, 0x99, 0x53, 0x9f, 0xc5, 0x9a, 0xd4, 0x36, 0xa3, 0xc6, + 0x5e, 0x8d, 0xbe, 0xd5, 0xdc, 0x76, 0xb4, 0x74, 0xb8, 0x26, 0x18, 0x27, + 0xfb, 0xf2, 0xfb, 0xd0, 0x9b, 0x3d, 0x7f, 0x10, 0xe2, 0xab, 0x44, 0xc7, + 0x88, 0x7f, 0xb4, 0x3d, 0x3e, 0xa3, 0xff, 0x6d, 0x06, 0x4b, 0x3e, 0x55, + 0xb2, 0x84, 0xf4, 0xad, 0x54, 0x88, 0x81, 0xc3, 0x9c, 0xf8, 0xb6, 0x68, + 0x96, 0x38, 0x8b, 0xcd, 0x90, 0x6d, 0x25, 0x4b, 0xbf, 0x0c, 0x44, 0x90, + 0xa5, 0x5b, 0x98, 0xd0, 0x40, 0x2f, 0xbb, 0x0d, 0xa8, 0x4b, 0x8a, 0x62, + 0x82, 0x46, 0x46, 0x18, 0x38, 0xae, 0x82, 0x07, 0xd0, 0xb4, 0x2f, 0x16, + 0x79, 0x55, 0x9f, 0x1b, 0xc5, 0x08, 0x6d, 0x85, 0xdf, 0x3f, 0xa9, 0x9b, + 0x4b, 0xc6, 0x28, 0xd3, 0x58, 0x72, 0x3d, 0x37, 0x11, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x78, 0x30, 0x76, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0e, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, + 0x30, 0x16, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x01, 0x01, 0xff, 0x04, 0x0c, + 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03, + 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x6c, + 0xe4, 0x6c, 0x27, 0xaa, 0xcd, 0x0d, 0x4b, 0x74, 0x21, 0xa4, 0xf6, 0x5f, + 0x87, 0xb5, 0x31, 0xfe, 0x10, 0xbb, 0xa7, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe8, 0x6a, 0x1c, 0xab, + 0x2c, 0x48, 0xf9, 0x60, 0x36, 0xa2, 0xf0, 0x7b, 0x8e, 0xd2, 0x9d, 0xb4, + 0x2a, 0x28, 0x98, 0xc8, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0x55, 0x34, 0xe2, 0xfa, 0xf6, 0x89, 0x86, 0xad, 0x92, 0x21, 0xec, 0xb9, + 0x54, 0x0e, 0x18, 0x47, 0x0d, 0x1b, 0xa7, 0x58, 0xad, 0x69, 0xe4, 0xef, + 0x3b, 0xe6, 0x8d, 0xdd, 0xda, 0x0c, 0x45, 0xf6, 0xe8, 0x96, 0xa4, 0x29, + 0x0f, 0xbb, 0xcf, 0x16, 0xae, 0x93, 0xd0, 0xcb, 0x2a, 0x26, 0x1a, 0x7b, + 0xfc, 0x51, 0x22, 0x76, 0x98, 0x31, 0xa7, 0x0f, 0x29, 0x35, 0x79, 0xbf, + 0xe2, 0x4f, 0x0f, 0x14, 0xf5, 0x1f, 0xcb, 0xbf, 0x87, 0x65, 0x13, 0x32, + 0xa3, 0x19, 0x4a, 0xd1, 0x3f, 0x45, 0xd4, 0x4b, 0xe2, 0x00, 0x26, 0xa9, + 0x3e, 0xd7, 0xa5, 0x37, 0x9f, 0xf5, 0xad, 0x61, 0xe2, 0x40, 0xa9, 0x74, + 0x24, 0x53, 0xf2, 0x78, 0xeb, 0x10, 0x9b, 0x2c, 0x27, 0x88, 0x46, 0xcb, + 0xe4, 0x60, 0xca, 0xf5, 0x06, 0x24, 0x40, 0x2a, 0x97, 0x3a, 0xcc, 0xd0, + 0x81, 0xb1, 0x15, 0xa3, 0x4f, 0xd0, 0x2b, 0x4f, 0xca, 0x6e, 0xaa, 0x24, + 0x31, 0xb3, 0xac, 0xa6, 0x75, 0x05, 0xfe, 0x8a, 0xf4, 0x41, 0xc4, 0x06, + 0x8a, 0xc7, 0x0a, 0x83, 0x4e, 0x49, 0xd4, 0x3f, 0x83, 0x50, 0xec, 0x57, + 0x04, 0x97, 0x14, 0x49, 0xf5, 0xe1, 0xb1, 0x7a, 0x9c, 0x09, 0x4f, 0x61, + 0x87, 0xc3, 0x97, 0x22, 0x17, 0xc2, 0xeb, 0xcc, 0x32, 0x81, 0x31, 0x21, + 0x3f, 0x10, 0x57, 0x5b, 0x43, 0xbe, 0xcd, 0x68, 0x82, 0xbe, 0xe5, 0xc1, + 0x65, 0x94, 0x7e, 0xc2, 0x34, 0x76, 0x2b, 0xcf, 0x89, 0x3c, 0x2b, 0x81, + 0x23, 0x72, 0x95, 0xcf, 0xc9, 0x67, 0x19, 0x2a, 0xd5, 0x5c, 0xca, 0xa3, + 0x46, 0xbd, 0x48, 0x06, 0x0b, 0xa6, 0xa3, 0x96, 0x50, 0x28, 0xc7, 0x7e, + 0xcf, 0x62, 0xf2, 0xfa, 0xc4, 0xf2, 0x53, 0xe3, 0xc9, 0xe8, 0x2e, 0xdd, + 0x29, 0x37, 0x07, 0x47, 0xff, 0xff, 0x8a, 0x32, 0xbd, 0xa2, 0xb7, 0x21, + 0x89, 0xa0, 0x55, 0xf7 +}; +unsigned int certificate_eku_der_len = 916; diff --git a/grub-core/tests/lib/functional_test.c b/grub-core/tests/lib/functional_test.c index 38e981f2c..23048919d 100644 --- a/grub-core/tests/lib/functional_test.c +++ b/grub-core/tests/lib/functional_test.c @@ -73,6 +73,7 @@ grub_functional_all_tests (grub_extcmd_context_t ctxt __attribute__ ((unused)), grub_dl_load ("xnu_uuid_test"); grub_dl_load ("pbkdf2_test"); grub_dl_load ("signature_test"); + grub_dl_load ("appended_signature_test"); grub_dl_load ("sleep_test"); grub_dl_load ("bswap_test"); grub_dl_load ("ctz_test"); From 0b59d379fc2f5833134195ba767297d8efd1e31f Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Mon, 6 Oct 2025 12:55:03 +0530 Subject: [PATCH 050/208] docs/grub: Document signing GRUB under UEFI Before adding information about how GRUB is signed with an appended signature scheme, it's worth adding some information about how it can currently be signed for UEFI. Signed-off-by: Daniel Axtens Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Stefan Berger Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- docs/grub.texi | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/grub.texi b/docs/grub.texi index 1c33b44e2..b796b4796 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -8907,6 +8907,7 @@ environment variables and commands are listed in the same order. * Measured Boot:: Measuring boot components * Lockdown:: Lockdown when booting on a secure setup * TPM2 key protector:: Managing disk key with TPM2 key protector +* Signing GRUB itself:: Ensuring the integrity of the GRUB core image @end menu @node Authentication and authorisation @@ -8987,7 +8988,7 @@ commands. GRUB's @file{core.img} can optionally provide enforcement that all files subsequently read from disk are covered by a valid digital signature. -This document does @strong{not} cover how to ensure that your +This section does @strong{not} cover how to ensure that your platform's firmware (e.g., Coreboot) validates @file{core.img}. If environment variable @code{check_signatures} @@ -9595,6 +9596,21 @@ which increases the risk of password leakage during the process. Moreover, the superuser list must be well maintained, and the password used cannot be synchronized with LUKS key rotation. +@node Signing GRUB itself +@section Signing GRUB itself +To ensure a complete secure-boot chain, there must be a way for the code that +loads GRUB to verify the integrity of the core image. +This is ultimately platform-specific and individual platforms can define their +own mechanisms. However, there are general-purpose mechanisms that can be used +with GRUB. +@section Signing GRUB for UEFI secure boot +On UEFI platforms, @file{core.img} is a PE binary. Therefore, it can be signed +with a tool such as @command{pesign} or @command{sbsign}. Refer to the +suggestions in @pxref{UEFI secure boot and shim} to ensure that the final +image works under UEFI secure boot and can maintain the secure-boot chain. It +will also be necessary to enroll the public key used into a relevant firmware +key database. + @node Platform limitations @chapter Platform limitations From 0f2dda8cf68cb36dc76a884f5a56d22df5f37b44 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:55:04 +0530 Subject: [PATCH 051/208] docs/grub: Document signing GRUB with an appended signature Signing GRUB for firmware that verifies an appended signature is a bit fiddly. I don't want people to have to figure it out from scratch so document it here. Signed-off-by: Daniel Axtens Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Stefan Berger Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- docs/grub.texi | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/docs/grub.texi b/docs/grub.texi index b796b4796..c462c68e7 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -9611,6 +9611,101 @@ image works under UEFI secure boot and can maintain the secure-boot chain. It will also be necessary to enroll the public key used into a relevant firmware key database. +@section Signing GRUB with an appended signature +The @file{core.elf} itself can be signed with a Linux kernel module-style +appended signature (@pxref{Using appended signatures}). +To support IEEE1275 platforms where the boot image is often loaded directly +from a disk partition rather than from a file system, the @file{core.elf} +can specify the size and location of the appended signature with an ELF +Note added by @command{grub-install} or @command{grub-mkimage}. +An image can be signed this way using the @command{sign-file} command from +the Linux kernel: + +@itemize +@item Signing a GRUB image using a single signer key. The grub.key is your +private key used for GRUB signing, grub.der is a corresponding public key +(certificate) used for GRUB signature verification, and the kernel.der is +your public key (certificate) used for kernel signature verification. +@example +@group +# Determine the size of the appended signature. It depends on the +# signing key and the hash algorithm. +# +# Signing /dev/null with an appended signature. + +sign-file SHA256 grub.key grub.der /dev/null ./empty.sig + +# Build a GRUB image for the signature. + +grub-mkimage -O powerpc-ieee1275 -o core.elf.unsigned -x kernel.der \ + -p /grub --appended-signature-size $(stat -c '%s' ./empty.sig) \ + --modules="appendedsig ..." ... + +# Remove the signature file. + +rm ./empty.sig + +# Signing a GRUB image with an appended signature. + +sign-file SHA256 grub.key grub.der core.elf.unsigned core.elf.signed + +@end group +@end example +@item Signing a GRUB image using more than one signer key. The grub1.key and +grub2.key are private keys used for GRUB signing, grub1.der and grub2.der +are corresponding public keys (certificates) used for GRUB signature verification. +The kernel1.der and kernel2.der are your public keys (certificates) used for +kernel signature verification. +@example +@group +# Generate a signature by signing /dev/null. + +openssl cms -sign -binary -nocerts -in /dev/null -signer \ + grub1.der -inkey grub1.key -signer grub2.der -inkey grub2.key \ + -out ./empty.p7s -outform DER -noattr -md sha256 + +# To be able to determine the size of an appended signature, sign an +# empty file (/dev/null) to which a signature will be appended to. + +sign-file -s ./empty.p7s sha256 /dev/null /dev/null ./empty.sig + +# Build a GRUB image for the signature. + +grub-mkimage -O powerpc-ieee1275 -o core.elf.unsigned -x kernel1.der \ + kernel2.der -p /grub --appended-signature-size $(stat -c '%s' ./empty.sig) \ + --modules="appendedsig ..." ... + +# Remove the signature files. + +rm ./empty.sig ./empty.p7s + +# Generate a raw signature for GRUB image signing using OpenSSL. + +openssl cms -sign -binary -nocerts -in core.elf.unsigned -signer \ + grub1.der -inkey grub1.key -signer grub2.der -inkey grub2.key \ + -out core.p7s -outform DER -noattr -md sha256 + +# Sign a GRUB image to get an image file with an appended signature. + +sign-file -s core.p7s sha256 /dev/null core.elf.unsigned core.elf.signed + +@end group +@end example +@item Don't forget to install the signed image as required +(e.g. on powerpc-ieee1275, to the PReP partition). +@example +@group +# Install signed GRUB image to the PReP partition on powerpc-ieee1275 + +dd if=core.elf.signed of=/dev/sda1 + +@end group +@end example +@end itemize + +As with UEFI secure boot, it is necessary to build-in the required modules, +or sign them if they are not part of the GRUB image. + @node Platform limitations @chapter Platform limitations From 3dff10a9796fc60d1464e1ba7a1025f791a62584 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Mon, 6 Oct 2025 12:55:05 +0530 Subject: [PATCH 052/208] docs/grub: Document appended signature This explains how appended signatures can be used to form part of a secure boot chain, and documents the commands and variables introduced. Signed-off-by: Daniel Axtens Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Stefan Berger Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- docs/grub.texi | 325 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 325 insertions(+) diff --git a/docs/grub.texi b/docs/grub.texi index c462c68e7..7cd5dee06 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -3281,8 +3281,10 @@ GRUB. Others may be used freely in GRUB configuration files. These variables have special meaning to GRUB. @menu +* appendedsig_key_mgmt:: * biosnum:: * blsuki_save_default:: +* check_appended_signatures:: * check_signatures:: * chosen:: * cmdpath:: @@ -3333,6 +3335,19 @@ These variables have special meaning to GRUB. @end menu +@node appendedsig_key_mgmt +@subsection appendedsig_key_mgmt + +This variable controls whether GRUB enforces appended signature validation +using either @code{static} or @code{dynamic} key management. It is automatically +set by GRUB to either @code{static} or @code{dynamic} based on the +@strong{'ibm,secure-boot'} device tree property and Platform KeyStore (PKS). +Also, it can be explicitly set to either @code{static} or @code{dynamic} by +setting the @code{appendedsig_key_mgmt} variable from the GRUB console +when the GRUB is not locked down. + +@xref{Using appended signatures} for more information. + @node biosnum @subsection biosnum @@ -3353,6 +3368,17 @@ If this variable is set, menu entries generated from BLS config files (@pxref{blscfg}) or UKI files (@pxref{uki}) will be set as the default boot entry when selected. +@node check_appended_signatures +@subsection check_appended_signatures + +This variable controls whether GRUB enforces appended signature validation on +loaded kernel and GRUB module files. It is automatically set by GRUB +to either @code{no} or @code{yes} based on the @strong{'ibm,secure-boot'} device +tree property. Also, it can be explicitly set to either @code{no} or @code{yes} by +setting the @code{check_appended_signatures} variable from the GRUB console +when the GRUB is not locked down. + +@xref{Using appended signatures} for more information. @node check_signatures @subsection check_signatures @@ -6546,6 +6572,13 @@ you forget a command, you can run the command @command{help} @menu * [:: Check file types and compare values * acpi:: Load ACPI tables +* append_add_db_cert:: Add trusted certificate to the db list +* append_add_db_hash:: Add trusted certificate/binary hash to the db list +* append_add_dbx_cert:: Add distrusted certificate to the dbx list +* append_add_dbx_hash:: Add distrusted certificate/binary hash to the dbx list +* append_list_db:: List all trusted certificates from the db list +* append_list_dbx:: List all distrusted certificates and binary/certificate hashes from the dbx list +* append_verify:: Verify appended digital signature using db and dbx lists * authenticate:: Check whether user is in user list * background_color:: Set background color for active terminal * background_image:: Load background image for active terminal @@ -6669,6 +6702,140 @@ Note: The command is not allowed when lockdown is enforced (@pxref{Lockdown}). unsigned code. @end deffn +@node append_add_db_cert +@subsection append_add_db_cert + +@deffn Command append_add_db_cert +Read an X.509 certificate from the file @var{X509_certificate} +and add it to GRUB's internal db list of trusted certificates. +These certificates are used to validate appended signatures when the +environment variable @code{check_appended_signatures} (@pxref{check_appended_signatures}) +is set to @code{yes} or the @command{append_verify} (@pxref{append_verify}) +command is executed from the GRUB console. + +@xref{Using appended signatures} for more information. +@end deffn + +@node append_add_db_hash +@subsection append_add_db_hash + +@deffn Command append_add_db_hash +Read a binary hash from the file @var{hash_file} +and add it to GRUB's internal db list of trusted binary hashes. These +hashes are used to validate the Linux kernel/GRUB module binary hashes when the +environment variable @code{check_appended_signatures} +(@pxref{check_appended_signatures}) is set to @code{yes} or the +@command{append_verify} (@pxref{append_verify}) command is executed +from the GRUB console. + +Here is an example for how to generate a SHA-256 hash for a file. The hash +will be in binary format: + +@example + +# The vmlinux (kernel image) file is your binary file, and +# it should be unsigned. +# +# Generate the binary_hash.bin file from the vmlinux file +# using OpenSSL command + +openssl dgst -binary -sha256 -out binary_hash.bin vmlinux + +@end example + +@xref{Using appended signatures} for more information. +@end deffn + +@node append_add_dbx_cert +@subsection append_add_dbx_cert + +@deffn Command append_add_dbx_cert +Read an X.509 certificate from the file @var{X509_certificate} +and add it to GRUB's internal dbx list of distrusted certificates. +These certificates are used to ensure that the distrusted certificates +are rejected during appended signatures validation when the environment +variable @code{check_appended_signatures} is set to @code{yes} +(@pxref{check_appended_signatures}) or the @command{append_verify} +(@pxref{append_verify}) command is executed from the GRUB console. +Also, these certificates are used to prevent distrusted certificates from +being added to the db list later on. + +@xref{Using appended signatures} for more information. +@end deffn + +@node append_add_dbx_hash +@subsection append_add_dbx_hash + +@deffn Command append_add_dbx_hash [@option{-b}|@option{-c}] +Read a binary/certificate hash from the file @var{hash_file} +and add it to GRUB's internal dbx list of distrusted binary/certificate hashes. +When the environment variable @code{check_appended_signatures} (@pxref{check_appended_signatures}) +is set to @code{yes} or the @command{append_verify} (@pxref{append_verify}) command +is executed from the GRUB console, then matching distrusted binary hashes or the signature +validation with distrusted certificates may lead to the rejection of the Linux kernel or GRUB modules. +Also, these hashes are used to prevent distrusted certificates and binary hashes from being +added to the db list later on. + +The @option{-b} (@option{--binary-hash}) can be used to specify a binary hash file and +@option{-c} (@option{--cert-hash}) can be used to specify a certificate hash file. + +Here is an example for how to generate a SHA-256 hash for a binary and a +certificate file. The hash will be in binary format: + +@example + +# The vmlinux (kernel image) file is your binary file, and +# it should be unsigned. The kernel.der is your certificate file. +# +# Generate the cert_hash.bin file from the kernel.der file + +openssl dgst -binary -sha256 -out cert_hash.bin kernel.der + +# Generate the binary_hash.bin file from the vmlinux file + +openssl dgst -binary -sha256 -out binary_hash.bin vmlinux + +@end example + +@xref{Using appended signatures} for more information. +@end deffn + +@node append_list_db +@subsection append_list_db + +@deffn Command append_list_db +List all X.509 certificates and binary hashes trusted by GRUB for validating +appended signatures. The output is a numbered list of certificates and binary hashes, +showing the certificate's version, serial number, issuer, subject, +public key algorithm, RSA public key size, and certificate fingerprint. + +@xref{Using appended signatures} for more information. +@end deffn + +@node append_list_dbx +@subsection append_list_dbx + +@deffn Command append_list_dbx +List all the distrusted X.509 certificates and binary/certificate hashes. +The output is a numbered list of certificates and binary/certificate hashes, +showing the certificate's version, serial number, issuer, subject, +public key algorithm, RSA public key size, and certificate fingerprint. + +@xref{Using appended signatures} for more information. +@end deffn + +@node append_verify +@subsection append_verify + +@deffn Command append_verify +Verifies an appended signature on @var{signed_file} against the trusted X.509 certificates +and hashes known to GRUB (@pxref{append_list_db},@pxref{append_list_dbx}, @pxref{append_add_db_cert}, +@pxref{append_add_db_hash}, @pxref{append_add_dbx_hash} and @pxref{append_add_dbx_cert}). +Exit code @code{$?} is set to 0 if the signature validates successfully. +If validation fails, it is set to a non-zero value. + +@xref{Using appended signatures} for more information. +@end deffn @node authenticate @subsection authenticate @@ -7507,6 +7674,13 @@ configurations, but to allow the user to select from among multiple configurations, and to enable ``one-shot'' boot attempts and ``savedefault'' behavior. @xref{Using GPG-style digital signatures}, for more information. + +If the environment variable @code{check_appended_signatures} value is set to +@code{yes} and GRUB is in lockeddown mode, the user is not allowed to set +@code{check_appended_signatures} to @code{no} and @code{appendedsig_key_mgmt} +to @code{static} or @code{dynamic} either directly using @command{load_env} +command or via environment block file. @xref{Using appended signatures}, for +more information. @end deffn @@ -8902,11 +9076,13 @@ environment variables and commands are listed in the same order. @menu * Authentication and authorisation:: Users and access control * Using GPG-style digital signatures:: Booting digitally signed code +* Using appended signatures:: An alternative approach to booting digitally signed code * UEFI secure boot and shim:: Booting digitally signed PE files * Secure Boot Advanced Targeting:: Embedded information for generation number based revocation * Measured Boot:: Measuring boot components * Lockdown:: Lockdown when booting on a secure setup * TPM2 key protector:: Managing disk key with TPM2 key protector +* Signing certificate and hash files:: Certificate and hash file signing * Signing GRUB itself:: Ensuring the integrity of the GRUB core image @end menu @@ -9067,6 +9243,127 @@ or BIOS) configuration to cause the machine to boot from a different (attacker-controlled) device. GRUB is at best only one link in a secure boot chain. +@node Using appended signatures +@section Using appended signatures in GRUB + +GRUB supports verifying Linux-style 'appended signatures' for Linux on Power LPAR +secure boot. Appended signatures are PKCS#7 messages containing a signature over the +contents of a file, plus some metadata, appended to the end of a file. A file +with an appended signature ends with the magic string: + +@example +~Module signature appended~\n +@end example + +where @code{\n} represents the line feed character, @code{0x0a}. + +Linux on Power LPAR secure boot is controlled by @strong{'ibm,secure-boot'} +device tree property and if this property is set to @code{2} (@samp{enforce}), +GRUB enters lockdown mode. There are three secure boot modes. They are + +@itemize +@item @samp{0 - disabled}: Secure boot is disabled. This is the default. +@item @samp{1 - audit}: Enforce signature verification by setting + @code{check_appended_signatures} (@pxref{check_appended_signatures}) to + @code{yes} and do not enter lockdown mode. Signature verification + is performed and if signature verification fails, display the errors and + allow the boot to continue. +@item @samp{2 - enforce}: Enter lockdown mode and enforce signature verification by setting + @code{check_appended_signatures} (@pxref{check_appended_signatures}) to @code{yes}. +@end itemize + +Note that Linux on Power LPAR only supports @samp{0 - disabled} and @samp{2 - enforce}, +and @samp{1 - audit} is considered as secure boot being disabled. + +Enforcement of signature verification is controlled by the environment variable +@code{check_appended_signatures} (@pxref{check_appended_signatures}). + +@itemize +@item @samp{no}: No verification is performed. This is the default. +@item @samp{yes}: Signature verification is performed and if signature verification fails, + display the errors and stop the boot. Signature verification cannot be disabled by setting + the @code{check_appended_signatures} variable back to @samp{no}. +@end itemize + +To enable appended signature verification, load the appendedsig module and an +X.509 certificate for verification. It is recommended to build the appendedsig module +into the core GRUB image. + +Key management is controlled by the environment variable @code{appendedsig_key_mgmt} +(@pxref{appendedsig_key_mgmt}). + +@itemize +@item @samp{static}: Enforce static key management signature verification. This is the default. + When GRUB is in lockdown mode, then the user cannot change the value of the + @code{appendedsig_key_mgmt}. +@item @samp{dynamic}: Enforce dynamic key management signature verification. When GRUB is in + lockdown mode, then the user cannot change the value of the @code{appendedsig_key_mgmt}. +@end itemize + +In static key management mode, certificates will be built into the core image using +the @code{--x509} parameter to @command{grub-mkimage}. The list of trusted certificates +available at boot time can be shown using @command{append_list_db} (@pxref{append_list_db}). +Distrusted certificates can be explicitly removed from the db using @command{append_add_dbx_cert} +(@pxref{append_add_dbx_cert}). Also, trusted certificates can be explicitly added to the db using +@command{append_add_db_cert} (@pxref{append_add_db_cert}). + +In dynamic key management mode, db and dbx are read from the Platform KeyStore (PKS). If +db does not exist in PKS, static keys (built-in keys) are used as the default keys. +The list of trusted certificates and binary hashes available at boot time can be shown using +@command{append_list_db} (@pxref{append_list_db}) and the list of distrusted certificates and +binary/certificate hashes available at boot time can be shown using @command{append_list_dbx} +(@pxref{append_list_dbx}). The trusted certificates and binary hashes can be explicitly added +to the db using @command{append_add_db_cert} (@pxref{append_add_db_cert}) and +@command{append_add_db_hash} (@pxref{append_add_db_hash}). Distrusted certificates can be explicitly +added to the dbx using @command{append_add_dbx_cert} (@pxref{append_add_dbx_cert}) and distrusted +certificate/binary hashes can be explicitly added to the dbx using @command{append_add_dbx_hash} +(@pxref{append_add_dbx_hash}). + +A file can be explicitly verified using @command{append_verify} (@pxref{append_verify}). + +Note that when the environment variable @code{check_appended_signatures} is set to @code{yes}, +the @command{append_add_db_cert} and @command{append_add_dbx_cert} commands only accept +the file @samp{@var{X509_certificate}} that is signed with an appended signature +(@pxref{Signing certificate and hash files}), and the @command{append_add_db_hash} and +@command{append_add_dbx_hash} commands only accept the file @samp{@var{hash_file}} that is +signed with an appended signature (@pxref{Signing certificate and hash files}). +The signature is verified by the appendedsig module. +When the environment variable @code{check_appended_signatures} is set to @code{no}, +these commands accept files without an appended signature. + +Also, note that @samp{@var{X509_certificate}} should be in DER-format and @samp{@var{hash_file}} +should be in binary format. Only SHA-256, SHA-384, or SHA-512 hashes of binary/certificate are allowed. +Certificates/hashes of certificates/binaries added through @command{append_add_db_cert}, +@command{append_add_dbx_cert}, @command{append_add_db_hash}, and @command{append_add_dbx_hash} +will not be persisted across boots. + +Only signatures created using SHA-256 or SHA-512 hash algorithm along with RSA keys of size 2048, +3072, or 4096 bits are supported. + +A file can be signed with the @command{sign-file} utility supplied with the +Linux kernel source. For example, if you have @code{signing.key} as the private +key and @code{certificate.der} as the X.509 certificate containing the public key: + +@example +sign-file SHA256 signing.key certificate.der vmlinux vmlinux.signed +@end example + +Once signature verification is turned on, the following file types must carry +appended signatures: + +@enumerate +@item Linux kernels +@item GRUB modules, except those built in to the core image +@item Any new certificate or binary hash files to be trusted +@item Any new certificate/binary hash files to be distrusted +@end enumerate + +When GRUB is in lockdown mode (when secure boot mode is set to @code{enforce}), +signature verification cannot be @strong{disabled} by setting the +@code{check_appended_signatures} (@pxref{check_appended_signatures}) variable +to @code{no} or using the @command{load_env} (@pxref{load_env}) command from +the GRUB console. + @node UEFI secure boot and shim @section UEFI secure boot and shim support @@ -9596,6 +9893,34 @@ which increases the risk of password leakage during the process. Moreover, the superuser list must be well maintained, and the password used cannot be synchronized with LUKS key rotation. +@node Signing certificate and hash files +@section Signing certificate and hash files +X.509 certificate (public key) files and hash files (binary/certificate hash files) +can be signed with a Linux kernel module-style appended signature. + +The signer.key is a private key used for signing and signer.der is the corresponding +public key (certificate) used for appended signature verification. Note that the +signer.der (certificate) should exist in the db (@pxref{Using appended signatures}). + +@itemize +@item Signing the X.509 certificate file using @file{sign-file}. +The kernel.der is an X.509 certificate file. +@example + +sign-file SHA256 signer.key signer.der kernel.der \ + kernel.der.signed + +@end example +@item Signing the hash file using @file{sign-file}. +The binary_hash.bin is a binary hash file. +@example + +sign-file SHA256 signer.key signer.der binary_hash.bin \ + binary_hash.signed + +@end example +@end itemize + @node Signing GRUB itself @section Signing GRUB itself To ensure a complete secure-boot chain, there must be a way for the code that From 8abbafa493d5d3f40de962df26b0a4ca8a95fdc0 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Tue, 2 Sep 2025 14:58:50 +0800 Subject: [PATCH 053/208] net/tcp: Fix TCP port number reused on reboot GRUB's TCP stack assigns source ports for outgoing connections starting at 21550 and increments sequentially by 1 (e.g., 21550, 21551, ...). While this generally works, it can lead to failures if the system reboots rapidly and reuses the same source port too soon. This issue was observed on powerpc-ieee1275 platforms using CAS (Client Architecture Support) reboot. In such cases, loading the initrd over HTTP may fail with connection timeouts. Packet captures show the failed connections are flagged as "TCP Port Number Reused" by Wireshark. The root cause is that GRUB reuses the same port shortly after reboot, while the server may still be tracking the previous connection in TIME_WAIT. This can result in the server rejecting the connection attempt or responding with a stale ACK or RST, leading to handshake failure. This patch fixes the issue by introducing a time based source port selection strategy. Instead of always starting from port 21550, GRUB now computes an initial base port based on the current RTC time, divided into 5 minute windows. The purpose of this time based strategy is to ensure that GRUB avoids reusing the same source port within a 5 minute window, thereby preventing collisions with stale server side connection tracking that could interfere with a new TCP handshake. A step size of 8 ensures that the same port will not be reused across reboots unless GRUB opens more than 8 TCP connections per second on average, something that is highly unlikely. In typical usage, a GRUB boot cycle lasts about 15 seconds and may open fewer than 100 connections total, well below the reuse threshold. This makes the approach robust against short reboot intervals while keeping the logic simple and deterministic. Signed-off-by: Michael Chang Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/net/tcp.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index 93dee0caa..064c0ff08 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -22,6 +22,7 @@ #include #include #include +#include #define TCP_SYN_RETRANSMISSION_TIMEOUT GRUB_NET_INTERVAL #define TCP_SYN_RETRANSMISSION_COUNT GRUB_NET_TRIES @@ -552,6 +553,36 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock, return GRUB_ERR_NONE; } +/* + * Derive a time-based source port to avoid reusing the same port across + * reboots. This helps prevent failures caused by server side TCP state + * (e.g. TIME_WAIT) from interfering with new connections using the same socket. + * + * The base port starts at 21550 and increments every second by 8 across + * a 5 minute window (300 seconds), giving 2400 possible distinct base ports + * per window. In typical GRUB usage, the number of connections per boot is + * small, so reuse is effectively avoided. + */ +static grub_uint16_t +get_initial_base_port (void) +{ + grub_err_t err; + struct grub_datetime date; + grub_int64_t t = 0; + grub_uint64_t r = 0; + + err = grub_get_datetime (&date); + if (err != GRUB_ERR_NONE || !grub_datetime2unixtime (&date, &t)) + { + grub_errno = GRUB_ERR_NONE; + return 21550; + } + + grub_divmod64 (t, 300, &r); + + return 21550 + (r << 3); +} + grub_net_tcp_socket_t grub_net_tcp_open (char *server, grub_uint16_t out_port, @@ -569,13 +600,19 @@ grub_net_tcp_open (char *server, struct grub_net_network_level_interface *inf; grub_net_network_level_address_t gateway; grub_net_tcp_socket_t socket; - static grub_uint16_t in_port = 21550; + static grub_uint16_t in_port; struct grub_net_buff *nb; struct tcphdr *tcph; int i; grub_uint8_t *nbd; grub_net_link_level_address_t ll_target_addr; + if (!in_port) + { + in_port = get_initial_base_port (); + grub_dprintf ("net", "base port: %d\n", in_port); + } + err = grub_net_resolve_address (server, &addr); if (err) return NULL; From 75a20cc1449b8b5176b8d93189a2d559a5415ad6 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 23 Sep 2025 17:33:31 -0600 Subject: [PATCH 054/208] kern: Make grub_error() more verbose Signed-off-by: Peter Jones Reviewed-by: Daniel Kiper --- grub-core/kern/err.c | 13 +++++++++++-- include/grub/err.h | 7 +++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/grub-core/kern/err.c b/grub-core/kern/err.c index 53c734de7..aebfe0cf8 100644 --- a/grub-core/kern/err.c +++ b/grub-core/kern/err.c @@ -33,15 +33,24 @@ static struct grub_error_saved grub_error_stack_items[GRUB_ERROR_STACK_SIZE]; static int grub_error_stack_pos; static int grub_error_stack_assert; +#ifdef grub_error +#undef grub_error +#endif + grub_err_t -grub_error (grub_err_t n, const char *fmt, ...) +grub_error (grub_err_t n, const char *file, const int line, const char *fmt, ...) { va_list ap; + int m; grub_errno = n; + m = grub_snprintf (grub_errmsg, sizeof (grub_errmsg), "%s:%d:", file, line); + if (m < 0) + m = 0; + va_start (ap, fmt); - grub_vsnprintf (grub_errmsg, sizeof (grub_errmsg), _(fmt), ap); + grub_vsnprintf (grub_errmsg + m, sizeof (grub_errmsg) - m, _(fmt), ap); va_end (ap); return n; diff --git a/include/grub/err.h b/include/grub/err.h index 6ab905c93..4b02d136a 100644 --- a/include/grub/err.h +++ b/include/grub/err.h @@ -89,8 +89,11 @@ struct grub_error_saved extern grub_err_t EXPORT_VAR(grub_errno); extern char EXPORT_VAR(grub_errmsg)[GRUB_MAX_ERRMSG]; -grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *fmt, ...) - __attribute__ ((format (GNU_PRINTF, 2, 3))); +grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *file, const int line, const char *fmt, ...) + __attribute__ ((format (GNU_PRINTF, 4, 5))); + +#define grub_error(n, fmt, ...) grub_error (n, __FILE__, __LINE__, fmt, ##__VA_ARGS__) + void EXPORT_FUNC(grub_fatal) (const char *fmt, ...) __attribute__ ((noreturn)); void EXPORT_FUNC(grub_error_push) (void); int EXPORT_FUNC(grub_error_pop) (void); From 894241c8543eb0dd27b16962a640f38e9b0c6e19 Mon Sep 17 00:00:00 2001 From: Leo Sandoval Date: Tue, 23 Sep 2025 17:33:32 -0600 Subject: [PATCH 055/208] kern: Include function name on debug and error print functions With the following change, we see standard (grub_dprintf) and error (grub_error) logs with the function name embedded (see below) into the log which is particular useful when debugging: commands/efi/tpm.c:grub_tpm_measure:281:tpm: log_event, pcr = 8, size = 0xb, Including one more field on the print log impacts the binary sizes and in turn their respective distro packages. For Fedora rpm packages the increase is 20k approximately. Signed-off-by: Leo Sandoval Reviewed-by: Daniel Kiper --- grub-core/kern/err.c | 4 ++-- grub-core/kern/misc.c | 4 ++-- include/grub/err.h | 6 +++--- include/grub/misc.h | 5 +++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/grub-core/kern/err.c b/grub-core/kern/err.c index aebfe0cf8..ba04b57fb 100644 --- a/grub-core/kern/err.c +++ b/grub-core/kern/err.c @@ -38,14 +38,14 @@ static int grub_error_stack_assert; #endif grub_err_t -grub_error (grub_err_t n, const char *file, const int line, const char *fmt, ...) +grub_error (grub_err_t n, const char *file, const char *function, const int line, const char *fmt, ...) { va_list ap; int m; grub_errno = n; - m = grub_snprintf (grub_errmsg, sizeof (grub_errmsg), "%s:%d:", file, line); + m = grub_snprintf (grub_errmsg, sizeof (grub_errmsg), "%s:%s:%d:", file, function, line); if (m < 0) m = 0; diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index 258f91893..b9ac86c18 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -231,14 +231,14 @@ grub_debug_enabled (const char * condition) } void -grub_real_dprintf (const char *file, const int line, const char *condition, +grub_real_dprintf (const char *file, const char *function, const int line, const char *condition, const char *fmt, ...) { va_list args; if (grub_debug_enabled (condition)) { - grub_printf ("%s:%d:%s: ", file, line, condition); + grub_printf ("%s:%s:%d:%s: ", file, function, line, condition); va_start (args, fmt); grub_vprintf (fmt, args); va_end (args); diff --git a/include/grub/err.h b/include/grub/err.h index 4b02d136a..99e757a80 100644 --- a/include/grub/err.h +++ b/include/grub/err.h @@ -89,10 +89,10 @@ struct grub_error_saved extern grub_err_t EXPORT_VAR(grub_errno); extern char EXPORT_VAR(grub_errmsg)[GRUB_MAX_ERRMSG]; -grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *file, const int line, const char *fmt, ...) - __attribute__ ((format (GNU_PRINTF, 4, 5))); +grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *file, const char *function, const int line, const char *fmt, ...) + __attribute__ ((format (GNU_PRINTF, 5, 6))); -#define grub_error(n, fmt, ...) grub_error (n, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define grub_error(n, fmt, ...) grub_error (n, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) void EXPORT_FUNC(grub_fatal) (const char *fmt, ...) __attribute__ ((noreturn)); void EXPORT_FUNC(grub_error_push) (void); diff --git a/include/grub/misc.h b/include/grub/misc.h index 9522d7305..0d2bab7dd 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -35,7 +35,7 @@ #define ARRAY_SIZE(array) (sizeof (array) / sizeof (array[0])) #define COMPILE_TIME_ASSERT(cond) switch (0) { case 1: case !(cond): ; } -#define grub_dprintf(condition, ...) grub_real_dprintf(GRUB_FILE, __LINE__, condition, __VA_ARGS__) +#define grub_dprintf(condition, ...) grub_real_dprintf(GRUB_FILE, __FUNCTION__, __LINE__, condition, __VA_ARGS__) void *EXPORT_FUNC(grub_memmove) (void *dest, const void *src, grub_size_t n); char *EXPORT_FUNC(grub_strcpy) (char *dest, const char *src); @@ -413,9 +413,10 @@ grub_puts (const char *s) int EXPORT_FUNC(grub_puts_) (const char *s); int EXPORT_FUNC(grub_debug_enabled) (const char *condition); void EXPORT_FUNC(grub_real_dprintf) (const char *file, + const char *function, const int line, const char *condition, - const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 4, 5))); + const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 5, 6))); int EXPORT_FUNC(grub_printf) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2))); int EXPORT_FUNC(grub_printf_) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2))); int EXPORT_FUNC(grub_vprintf) (const char *fmt, va_list args); From 9a725391f1a70b8a0ae4acde8fbb22a78e52697d Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Sat, 20 Sep 2025 02:12:32 +0100 Subject: [PATCH 056/208] commands/efi/tpm: Call get_active_pcr_banks() only with TCG2 1.1 or newer The call was added in the 1.1 revision of the spec, 1.0 does not have it, and there are some machines out there with a TPM2 and a UEFI firmware that only supports version 1.0, so the call fails in those cases. Check the reported version before calling get_active_pcr_banks(). See Table 4 in section 6.2 of the TCG EFI Protocol Specification: https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specification-rev13-160330final.pdf Fixes: f326c5c47 (commands/bli: Set LoaderTpm2ActivePcrBanks runtime variable) Signed-off-by: Luca Boccassi Reviewed-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- grub-core/commands/efi/tpm.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/grub-core/commands/efi/tpm.c b/grub-core/commands/efi/tpm.c index 59d0b6708..7b493c890 100644 --- a/grub-core/commands/efi/tpm.c +++ b/grub-core/commands/efi/tpm.c @@ -39,6 +39,7 @@ static grub_uint8_t grub_tpm_version; static grub_int8_t tpm1_present = -1; static grub_int8_t tpm2_present = -1; +static grub_int8_t tpm2_pcr_banks_reporting_present = -1; static grub_efi_boolean_t grub_tpm1_present (grub_efi_tpm_protocol_t *tpm) @@ -89,6 +90,34 @@ grub_tpm2_present (grub_efi_tpm2_protocol_t *tpm) return (grub_efi_boolean_t) tpm2_present; } +static grub_efi_boolean_t +grub_tpm2_pcr_banks_reporting_present (grub_efi_tpm2_protocol_t *tpm) +{ + grub_efi_status_t status; + EFI_TCG2_BOOT_SERVICE_CAPABILITY caps; + + caps.Size = (grub_uint8_t) sizeof (caps); + + if (tpm2_pcr_banks_reporting_present != -1) + return (grub_efi_boolean_t) tpm2_pcr_banks_reporting_present; + + if (!grub_tpm2_present (tpm)) + return (grub_efi_boolean_t) (tpm2_pcr_banks_reporting_present = 0); + + status = tpm->get_capability (tpm, &caps); + + if (status != GRUB_EFI_SUCCESS || caps.StructureVersion.Major < 1 + || (caps.StructureVersion.Major == 1 && caps.StructureVersion.Minor < 1)) + tpm2_pcr_banks_reporting_present = 0; + else + tpm2_pcr_banks_reporting_present = 1; + + grub_dprintf ("tpm", "tpm2 PCR banks reporting%s present\n", + tpm2_pcr_banks_reporting_present ? "" : " NOT"); + + return (grub_efi_boolean_t) tpm2_pcr_banks_reporting_present; +} + static grub_efi_boolean_t grub_tpm_handle_find (grub_efi_handle_t *tpm_handle, grub_efi_uint8_t *protocol_version) @@ -355,7 +384,7 @@ grub_tpm2_active_pcr_banks (void) return 0; } - if (grub_tpm2_present (tpm)) + if (grub_tpm2_pcr_banks_reporting_present (tpm)) { grub_efi_status_t status = tpm->get_active_pcr_banks (tpm, &active_pcr_banks); From fa93f2412b260572675bd4b4dacb6a73d2b42471 Mon Sep 17 00:00:00 2001 From: Srish Srinivasan Date: Mon, 22 Sep 2025 11:32:46 +0530 Subject: [PATCH 057/208] kern/command,commands/extcmd: Perform explicit NULL check in both the unregister helpers During command registration, grub_register_command_prio() returns a 0 when there is a failure in memory allocation. In such a situation, calls to grub_unregister_{command(), extcmd()} during command unregistration will result in dereferencing a NULL pointer. Perform explicit NULL check in both unregister helpers to prevent undefined behaviour due to a NULL pointer dereference. Signed-off-by: Srish Srinivasan Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper --- grub-core/commands/extcmd.c | 3 +++ grub-core/kern/command.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/grub-core/commands/extcmd.c b/grub-core/commands/extcmd.c index c236be13a..7f8cb3f67 100644 --- a/grub-core/commands/extcmd.c +++ b/grub-core/commands/extcmd.c @@ -139,6 +139,9 @@ grub_register_extcmd_lockdown (const char *name, grub_extcmd_func_t func, void grub_unregister_extcmd (grub_extcmd_t ext) { + if (ext == NULL) + return; + grub_unregister_command (ext->cmd); grub_free (ext); } diff --git a/grub-core/kern/command.c b/grub-core/kern/command.c index 5812e131c..a1b4c81a9 100644 --- a/grub-core/kern/command.c +++ b/grub-core/kern/command.c @@ -104,6 +104,9 @@ grub_register_command_lockdown (const char *name, void grub_unregister_command (grub_command_t cmd) { + if (cmd == NULL) + return; + if ((cmd->prio & GRUB_COMMAND_FLAG_ACTIVE) && (cmd->next)) cmd->next->prio |= GRUB_COMMAND_FLAG_ACTIVE; grub_list_remove (GRUB_AS_LIST (cmd)); From c5ff0d616f4f311fde0b8da267beb749b92c2ebe Mon Sep 17 00:00:00 2001 From: Sridhar Markonda Date: Wed, 17 Sep 2025 16:24:09 +0530 Subject: [PATCH 058/208] docs: Fix build warnings in libgcrypt and blsuki doc Following warnings are thrown during libgrcypt and bluski doc build: grub.texi:4744: warning: node next pointer for `gcry_arcfour_module' is `gcry_blake2_module' but next is `gcry_aria_module' in menu grub.texi:4744: warning: node prev pointer for `gcry_arcfour_module' is `gcry_aria_module' but prev is `functional_test_module' in menu grub.texi:4751: warning: node prev pointer for `gcry_blake2_module' is `gcry_arcfour_module' but prev is `gcry_aria_module' in menu grub.texi:8532: warning: node next pointer for `trust' is `unset' but next is `uki' in menu grub.texi:8549: warning: node next pointer for `unset' is `uki' but next is `verify_detached' in menu grub.texi:8549: warning: node prev pointer for `unset' is `trust' but prev is `uki' in menu grub.texi:8557: warning: node next pointer for `uki' is `verify_detached' but next is `unset' in menu grub.texi:8557: warning: node prev pointer for `uki' is `unset' but prev is `trust' in menu grub.texi:8600: warning: node prev pointer for `verify_detached' is `uki' but prev is `unset' in menu Fix order of gcry_aria_module and unset nodes. Signed-off-by: Sridhar Markonda Reviewed-by: Andrew Hamilton Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- docs/grub.texi | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index 7cd5dee06..d697010ce 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -4736,11 +4736,6 @@ This module provides support functions (helper functions) for file systems. This module provides support for running the GRUB functional tests using commands @command{functional_test} and @command{all_functional_test}. -@node gcry_aria_module -@section gcry_aria -This module provides support for the ARIA cipher. -This GRUB module is based on libgcrypt. - @node gcry_arcfour_module @section gcry_arcfour This module provides support for the arcfour stream cipher also known as RC4. @@ -4748,6 +4743,11 @@ If security is a concern, RC4 / arcfour cipher is consider broken (multiple known vulnerabilities make this insecure). This GRUB module is based on libgcrypt. +@node gcry_aria_module +@section gcry_aria +This module provides support for the ARIA cipher. +This GRUB module is based on libgcrypt. + @node gcry_blake2_module @section gcry_blake2 This module provides support for the BLAKE2b and BLAKE2s message digests. @@ -8546,14 +8546,6 @@ information. @end deffn -@node unset -@subsection unset - -@deffn Command unset envvar -Unset the environment variable @var{envvar}. -@end deffn - - @node uki @subsection uki @@ -8586,6 +8578,14 @@ will be added to the GRUB menu. For more information on UKI, see: @uref{https://uapi-group.org/specifications/specs/unified_kernel_image/, The Unified Kernel Image Specification} @end deffn +@node unset +@subsection unset + +@deffn Command unset envvar +Unset the environment variable @var{envvar}. +@end deffn + + @ignore @node vbeinfo @subsection vbeinfo From a8379e693b00ed523aac96230c778c773fcbb684 Mon Sep 17 00:00:00 2001 From: Dave Vasilevsky Date: Wed, 1 Oct 2025 17:03:48 -0400 Subject: [PATCH 059/208] fs/hfsplus: Allow reading files created by Mac OS 9 The "permissions" field of hfsplus files is only used by Mac OS X. This causes GRUB to skip reading files created by Mac OS 9, since their file mode is read as unknown. Instead, assume files with zero mode are regular files. From Technote 1150: The traditional Mac OS implementation of HFS Plus does not use the permissions field. Files created by traditional Mac OS have the entire field set to 0. Signed-off-by: Dave Vasilevsky Reviewed-by: Daniel Kiper --- grub-core/fs/hfsplus.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/hfsplus.c b/grub-core/fs/hfsplus.c index 3f203abcc..12d3f2641 100644 --- a/grub-core/fs/hfsplus.c +++ b/grub-core/fs/hfsplus.c @@ -736,7 +736,9 @@ list_nodes (void *record, void *hook_arg) int mode = (grub_be_to_cpu16 (fileinfo->mode) & GRUB_HFSPLUS_FILEMODE_MASK); - if (mode == GRUB_HFSPLUS_FILEMODE_REG) + if (mode == 0) /* Created by pre-Mac OS X. */ + type = GRUB_FSHELP_REG; + else if (mode == GRUB_HFSPLUS_FILEMODE_REG) type = GRUB_FSHELP_REG; else if (mode == GRUB_HFSPLUS_FILEMODE_SYMLINK) type = GRUB_FSHELP_SYMLINK; From e1b9d92a8be7eb99e9f963a4b5280fea85b49877 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 2 Oct 2025 14:44:01 +0200 Subject: [PATCH 060/208] loader/i386/linux: Transfer EDID information to kernel The Linux kernel's struct bootparams provides a field at offset 0x140 for storing an EDID header. Copy the video adapter's data to the field. The edid_info field was added in 2003 (see "[FBDEV] EDID support from OpenFirmware on PPC platoforms and from the BIOS on intel platforms."), but only got useable in 2004 (see "[PATCH] Fix EDID_INFO in zero-page"). The boot protocol was at version 2.03 at that time. The field was never used much, but with the recent addition of the efidrm and vesadrm drivers to the kernel, it becomes much more useful. As with the initial screen setup, these drivers can make use of the provided EDID information for basic display output. Signed-off-by: Thomas Zimmermann Reviewed-by: Neal Gompa Reviewed-by: Michael Chang Reviewed-by: Daniel Kiper --- grub-core/loader/i386/linux.c | 16 ++++++++++++++++ grub-core/video/video.c | 21 +++++++++++++++++++++ include/grub/video.h | 2 ++ 3 files changed, 39 insertions(+) diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 12731feb2..d6e291b2c 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -234,6 +234,7 @@ grub_e820_add_region (struct grub_boot_e820_entry *e820_entry, int *e820_num, static grub_err_t grub_linux_setup_video (struct linux_kernel_params *params) { + struct grub_video_edid_info edid_info; struct grub_video_mode_info mode_info; void *framebuffer; grub_err_t err; @@ -245,6 +246,19 @@ grub_linux_setup_video (struct linux_kernel_params *params) if (driver_id == GRUB_VIDEO_DRIVER_NONE) return 1; + err = grub_video_get_edid (&edid_info); + if (err != GRUB_ERR_NONE) + grub_memset (&edid_info, 0, sizeof (edid_info)); + + /* + * We cannot transfer any extensions. Therefore clear + * the extension flag from the checksum and set the + * field to zero. Adding the extension flag to the + * checksum does the trick. + */ + edid_info.checksum += edid_info.extension_flag; + edid_info.extension_flag = 0; + err = grub_video_get_info_and_fini (&mode_info, &framebuffer); if (err) @@ -338,6 +352,8 @@ grub_linux_setup_video (struct linux_kernel_params *params) } #endif + grub_memcpy (params->edid_info, &edid_info, sizeof (params->edid_info)); + return GRUB_ERR_NONE; } diff --git a/grub-core/video/video.c b/grub-core/video/video.c index 8937da745..d5bd1d079 100644 --- a/grub-core/video/video.c +++ b/grub-core/video/video.c @@ -89,6 +89,27 @@ grub_video_get_info_and_fini (struct grub_video_mode_info *mode_info, return GRUB_ERR_NONE; } +/* Get information about connected display. */ +grub_err_t +grub_video_get_edid (struct grub_video_edid_info *edid_info) +{ + grub_err_t err; + + if (grub_video_adapter_active == NULL) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + if (grub_video_adapter_active->get_edid != NULL) + { + err = grub_video_adapter_active->get_edid (edid_info); + if (err != GRUB_ERR_NONE) + return err; + } + else + grub_memset (edid_info, 0, sizeof (*edid_info)); + + return GRUB_ERR_NONE; +} + /* Determine optimized blitting formation for specified video mode info. */ enum grub_video_blit_format grub_video_get_blit_format (struct grub_video_mode_info *mode_info) diff --git a/include/grub/video.h b/include/grub/video.h index 9dac0f379..761ee994a 100644 --- a/include/grub/video.h +++ b/include/grub/video.h @@ -445,6 +445,8 @@ grub_err_t EXPORT_FUNC (grub_video_get_info) (struct grub_video_mode_info *mode_ grub_err_t EXPORT_FUNC (grub_video_get_info_and_fini) (struct grub_video_mode_info *mode_info, void **framebuffer); +grub_err_t EXPORT_FUNC (grub_video_get_edid) (struct grub_video_edid_info *edid_info); + enum grub_video_blit_format EXPORT_FUNC(grub_video_get_blit_format) (struct grub_video_mode_info *mode_info); grub_err_t grub_video_set_palette (unsigned int start, unsigned int count, From b2549b4d34f86e1113db947d3d8d6e7bd341270b Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 3 Oct 2025 11:22:02 +0800 Subject: [PATCH 061/208] tss2: Add TPM2_PCR_Event command The TPM2_PCR_Event command is introduced to tss2 to allow the user to extend a specific PCR. The related data structure and unmarshal function are also introduced. However, simply invoking TPM2_PCR_Event does not automatically record the event into the TPM event log. The TPM event log is primarily maintained by the system firmware (e.g., BIOS/UEFI). Therefore, for most standard use cases, the recommended method for extending PCRs and ensuring proper event logging is to utilize the system firmware functions. There are specific scenarios where direct use of TPM2_PCR_Event becomes necessary. For instance, in environments lacking system firmware support for PCR extension, such as the grub-emu, TPM2_PCR_Event serves as the only available method to extend PCRs. Signed-off-by: Gary Lin Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/lib/tss2/tpm2_cmd.c | 51 +++++++++++++++++++++++++++++++ grub-core/lib/tss2/tpm2_cmd.h | 7 +++++ grub-core/lib/tss2/tss2_mu.c | 18 +++++++++++ grub-core/lib/tss2/tss2_mu.h | 4 +++ grub-core/lib/tss2/tss2_structs.h | 7 +++++ grub-core/lib/tss2/tss2_types.h | 1 + 6 files changed, 88 insertions(+) diff --git a/grub-core/lib/tss2/tpm2_cmd.c b/grub-core/lib/tss2/tpm2_cmd.c index 6d25db1ab..b682eb431 100644 --- a/grub-core/lib/tss2/tpm2_cmd.c +++ b/grub-core/lib/tss2/tpm2_cmd.c @@ -575,6 +575,57 @@ grub_tpm2_flushcontext (const TPMI_DH_CONTEXT_t handle) return TPM_RC_SUCCESS; } +TPM_RC_t +grub_tpm2_pcr_event (const TPMI_DH_PCR_t pcrHandle, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_EVENT_t *eventData, + TPML_DIGEST_VALUES_t *digests, + TPMS_AUTH_RESPONSE_t *authResponse) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPML_DIGEST_VALUES_t digestsTmp; + TPMS_AUTH_RESPONSE_t authResponseTmp; + TPM_RC_t responseCode; + grub_uint32_t parameterSize; + + if (eventData == NULL) + return TPM_RC_VALUE; + if (authCommand == NULL) + return TPM_RC_VALUE; + + if (digests == NULL) + digests = &digestsTmp; + if (authResponse == NULL) + authResponse = &authResponseTmp; + + /* Marshal */ + grub_tpm2_buffer_init (&in); + grub_tpm2_buffer_pack_u32 (&in, pcrHandle); + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + grub_Tss2_MU_TPM2B_Marshal (&in, eventData->size, eventData->buffer); + if (in.error == true) + return TPM_RC_FAILURE; + + /* Submit */ + grub_tpm2_buffer_init (&out); + rc = tpm2_submit_command (TPM_ST_SESSIONS, TPM_CC_PCR_Event, &responseCode, &in, &out); + if (rc != TPM_RC_SUCCESS) + return rc; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + grub_tpm2_buffer_unpack_u32 (&out, ¶meterSize); + grub_Tss2_MU_TPML_DIGEST_VALUE_Unmarshal (&out, digests); + grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); + if (out.error == true) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + TPM_RC_t grub_tpm2_pcr_read (const TPMS_AUTH_COMMAND_t *authCommand, const TPML_PCR_SELECTION_t *pcrSelectionIn, diff --git a/grub-core/lib/tss2/tpm2_cmd.h b/grub-core/lib/tss2/tpm2_cmd.h index 90b42efec..d7ad962ab 100644 --- a/grub-core/lib/tss2/tpm2_cmd.h +++ b/grub-core/lib/tss2/tpm2_cmd.h @@ -89,6 +89,13 @@ grub_tpm2_unseal (const TPMI_DH_OBJECT_t item_handle, extern TPM_RC_t grub_tpm2_flushcontext (const TPMI_DH_CONTEXT_t handle); +extern TPM_RC_t +grub_tpm2_pcr_event (const TPMI_DH_PCR_t pcrHandle, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_EVENT_t *eventData, + TPML_DIGEST_VALUES_t *digests, + TPMS_AUTH_RESPONSE_t *authResponse); + extern TPM_RC_t grub_tpm2_pcr_read (const TPMS_AUTH_COMMAND_t *authCommand, const TPML_PCR_SELECTION_t *pcrSelectionIn, diff --git a/grub-core/lib/tss2/tss2_mu.c b/grub-core/lib/tss2/tss2_mu.c index 816e5b37f..e544e62f9 100644 --- a/grub-core/lib/tss2/tss2_mu.c +++ b/grub-core/lib/tss2/tss2_mu.c @@ -1118,6 +1118,24 @@ grub_Tss2_MU_TPML_DIGEST_Unmarshal (grub_tpm2_buffer_t buffer, grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (buffer, &digest->digests[i]); } +void +grub_Tss2_MU_TPML_DIGEST_VALUE_Unmarshal (grub_tpm2_buffer_t buffer, + TPML_DIGEST_VALUES_t *digests) +{ + grub_uint32_t i; + + grub_tpm2_buffer_unpack_u32 (buffer, &digests->count); + + if (digests->count > TPM_NUM_PCR_BANKS) + { + buffer->error = true; + return; + } + + for (i = 0; i < digests->count; i++) + grub_Tss2_MU_TPMT_HA_Unmarshal (buffer, &digests->digests[i]); +} + void grub_Tss2_MU_TPMS_SIGNATURE_RSA_Unmarshal (grub_tpm2_buffer_t buffer, TPMS_SIGNATURE_RSA_t *rsa) diff --git a/grub-core/lib/tss2/tss2_mu.h b/grub-core/lib/tss2/tss2_mu.h index 6440de57c..76eebc994 100644 --- a/grub-core/lib/tss2/tss2_mu.h +++ b/grub-core/lib/tss2/tss2_mu.h @@ -380,6 +380,10 @@ extern void grub_Tss2_MU_TPML_DIGEST_Unmarshal (grub_tpm2_buffer_t buffer, TPML_DIGEST_t *digest); +extern void +grub_Tss2_MU_TPML_DIGEST_VALUE_Unmarshal (grub_tpm2_buffer_t buffer, + TPML_DIGEST_VALUES_t *digests); + extern void grub_Tss2_MU_TPMS_SIGNATURE_RSA_Unmarshal (grub_tpm2_buffer_t buffer, TPMS_SIGNATURE_RSA_t *p); diff --git a/grub-core/lib/tss2/tss2_structs.h b/grub-core/lib/tss2/tss2_structs.h index 2eefba87c..0ac09f50f 100644 --- a/grub-core/lib/tss2/tss2_structs.h +++ b/grub-core/lib/tss2/tss2_structs.h @@ -144,6 +144,13 @@ typedef struct TPML_DIGEST TPML_DIGEST_t; /* TPM2B_NONCE Type */ typedef TPM2B_DIGEST_t TPM2B_NONCE_t; +/* TPM2B_EVENT Structure */ +struct TPM2B_EVENT { + grub_uint16_t size; + grub_uint8_t buffer[1024]; +}; +typedef struct TPM2B_EVENT TPM2B_EVENT_t; + /* TPMA_SESSION Structure */ struct TPMA_SESSION { diff --git a/grub-core/lib/tss2/tss2_types.h b/grub-core/lib/tss2/tss2_types.h index bddde7191..52d304b90 100644 --- a/grub-core/lib/tss2/tss2_types.h +++ b/grub-core/lib/tss2/tss2_types.h @@ -343,6 +343,7 @@ typedef grub_uint32_t TPM_CC_t; #define TPM_CC_NV_Write ((TPM_CC_t) 0x00000137) #define TPM_CC_NV_UndefineSpace ((TPM_CC_t) 0x00000122) #define TPM_CC_GetCapability ((TPM_CC_t) 0x0000017a) +#define TPM_CC_PCR_Event ((TPM_CC_t) 0x0000013c) #define TPM_CC_PCR_Read ((TPM_CC_t) 0x0000017e) #define TPM_CC_Load ((TPM_CC_t) 0x00000157) #define TPM_CC_LoadExternal ((TPM_CC_t) 0x00000167) From d47d261ecda41c0c7b774a32b75e48bccede8336 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 3 Oct 2025 11:22:03 +0800 Subject: [PATCH 062/208] tss2: Introduce grub_tcg2_cap_pcr() This commit introduces the definition of grub_tcg2_cap_pcr(), a new function designed to enhance the security of sealed keys. Its primary purpose is to "cap" a specific PCR by extending it with an EV_SEPARATOR event. This action cryptographically alters the PCR value, making it impossible to unseal any key that was previously sealed to the original PCR state. Consequently, the sealed key remains protected against unauthorized unsealing attempts until the associated PCRs are reset to their initial configuration, typically occurring during a subsequent system boot. Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/lib/tss2/tcg2.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/grub-core/lib/tss2/tcg2.h b/grub-core/lib/tss2/tcg2.h index 3d26373dd..ba3d11278 100644 --- a/grub-core/lib/tss2/tcg2.h +++ b/grub-core/lib/tss2/tcg2.h @@ -23,6 +23,8 @@ #include #include +#define GRUB_EV_SEPARATOR 0x04 + extern grub_err_t grub_tcg2_get_max_output_size (grub_size_t *size); @@ -32,4 +34,7 @@ grub_tcg2_submit_command (grub_size_t input_size, grub_size_t output_size, grub_uint8_t *output); +extern grub_err_t +grub_tcg2_cap_pcr (grub_uint8_t pcr); + #endif /* ! GRUB_TPM2_TCG2_HEADER */ From 39f98e47196faab76f4e6691e9b244ce1b30c83f Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 3 Oct 2025 11:22:04 +0800 Subject: [PATCH 063/208] tss2: Implement grub_tcg2_cap_pcr() for EFI This commit implements grub_tcg2_cap_pcr() for EFI by using the UEFI TCG2 protocol, HashLogExtendEvent, to extend the specified PCR with an EV_SEPARATOR event and ensure the event will be recorded properly in the TPM event log. Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/lib/efi/tcg2.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/grub-core/lib/efi/tcg2.c b/grub-core/lib/efi/tcg2.c index 841bf50bb..215d2d7b6 100644 --- a/grub-core/lib/efi/tcg2.c +++ b/grub-core/lib/efi/tcg2.c @@ -22,6 +22,7 @@ #include #include +#include #include static grub_err_t @@ -141,3 +142,42 @@ grub_tcg2_submit_command (grub_size_t input_size, return GRUB_ERR_NONE; } + +grub_err_t +grub_tcg2_cap_pcr (grub_uint8_t pcr) +{ + grub_err_t err; + grub_efi_status_t status; + grub_efi_tpm2_protocol_t *protocol; + EFI_TCG2_EVENT *event; + grub_uint8_t separator[4] = {0}; + + if (pcr >= TPM_MAX_PCRS) + return GRUB_ERR_BAD_ARGUMENT; + + err = tcg2_get_protocol (&protocol); + if (err != GRUB_ERR_NONE) + return err; + + event = grub_zalloc (sizeof (EFI_TCG2_EVENT) + sizeof (separator)); + if (event == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("cannot allocate TPM event buffer")); + + event->Header.HeaderSize = sizeof (EFI_TCG2_EVENT_HEADER); + event->Header.HeaderVersion = 1; + event->Header.PCRIndex = pcr; + event->Header.EventType = GRUB_EV_SEPARATOR; + event->Size = sizeof (*event) - sizeof (event->Event) + sizeof (separator); + grub_memcpy (event->Event, separator, sizeof (separator)); + + status = protocol->hash_log_extend_event (protocol, 0, + (grub_addr_t) separator, + sizeof (separator), event); + grub_free (event); + + if (status != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot cap PCR %u"), pcr); + + return GRUB_ERR_NONE; +} From 7b39970e907af9e0b7efe7de9589f0e9cd4c7e15 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 3 Oct 2025 11:22:05 +0800 Subject: [PATCH 064/208] tss2: Implement grub_tcg2_cap_pcr() for ieee1275 This commit implements grub_tcg2_cap_pcr() for ieee1275 with the firmware function, 2hash-ext-log, to extend the target PCR with an EV_SEPARATOR event and record the event into the TPM event log. To avoid duplicate code, ibmvtpm_2hash_ext_log() is moved to tcg2.c and exported as a global function. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- grub-core/commands/ieee1275/ibmvtpm.c | 59 +++--------------------- grub-core/lib/ieee1275/tcg2.c | 65 +++++++++++++++++++++++++++ include/grub/ieee1275/tpm.h | 5 +++ 3 files changed, 76 insertions(+), 53 deletions(-) diff --git a/grub-core/commands/ieee1275/ibmvtpm.c b/grub-core/commands/ieee1275/ibmvtpm.c index 4958b04a9..665d90931 100644 --- a/grub-core/commands/ieee1275/ibmvtpm.c +++ b/grub-core/commands/ieee1275/ibmvtpm.c @@ -27,67 +27,20 @@ #include #include -static int -ibmvtpm_2hash_ext_log (grub_uint8_t pcrindex, - grub_uint32_t eventtype, - const char *description, - grub_size_t description_size, - void *buf, grub_size_t size) -{ - struct tpm_2hash_ext_log - { - struct grub_ieee1275_common_hdr common; - grub_ieee1275_cell_t method; - grub_ieee1275_cell_t ihandle; - grub_ieee1275_cell_t size; - grub_ieee1275_cell_t buf; - grub_ieee1275_cell_t description_size; - grub_ieee1275_cell_t description; - grub_ieee1275_cell_t eventtype; - grub_ieee1275_cell_t pcrindex; - grub_ieee1275_cell_t catch_result; - grub_ieee1275_cell_t rc; - }; - struct tpm_2hash_ext_log args; - - INIT_IEEE1275_COMMON (&args.common, "call-method", 8, 2); - args.method = (grub_ieee1275_cell_t) "2hash-ext-log"; - args.ihandle = grub_ieee1275_tpm_ihandle; - args.pcrindex = pcrindex; - args.eventtype = eventtype; - args.description = (grub_ieee1275_cell_t) description; - args.description_size = description_size; - args.buf = (grub_ieee1275_cell_t) buf; - args.size = (grub_ieee1275_cell_t) size; - - if (IEEE1275_CALL_ENTRY_FN (&args) == -1) - return -1; - - /* - * catch_result is set if firmware does not support 2hash-ext-log - * rc is GRUB_IEEE1275_CELL_FALSE (0) on failure - */ - if ((args.catch_result) || args.rc == GRUB_IEEE1275_CELL_FALSE) - return -1; - - return 0; -} - static grub_err_t tpm2_log_event (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, const char *description) { static int error_displayed = 0; - int rc; + grub_err_t err; - rc = ibmvtpm_2hash_ext_log (pcr, EV_IPL, - description, grub_strlen(description) + 1, - buf, size); - if (rc && !error_displayed) + err = grub_ieee1275_ibmvtpm_2hash_ext_log (pcr, EV_IPL, + description, grub_strlen(description) + 1, + buf, size); + if (err != GRUB_ERR_NONE && !error_displayed) { error_displayed++; - return grub_error (GRUB_ERR_BAD_DEVICE, - "2HASH-EXT-LOG failed: Firmware is likely too old.\n"); + return err; } return GRUB_ERR_NONE; diff --git a/grub-core/lib/ieee1275/tcg2.c b/grub-core/lib/ieee1275/tcg2.c index 40161c2f9..28e7db225 100644 --- a/grub-core/lib/ieee1275/tcg2.c +++ b/grub-core/lib/ieee1275/tcg2.c @@ -56,6 +56,52 @@ grub_ieee1275_tpm_init (void) return GRUB_ERR_NONE; } +grub_err_t +grub_ieee1275_ibmvtpm_2hash_ext_log (grub_uint8_t pcrindex, + grub_uint32_t eventtype, + const char *description, + grub_size_t description_size, + void *buf, grub_size_t size) +{ + struct tpm_2hash_ext_log + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t size; + grub_ieee1275_cell_t buf; + grub_ieee1275_cell_t description_size; + grub_ieee1275_cell_t description; + grub_ieee1275_cell_t eventtype; + grub_ieee1275_cell_t pcrindex; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t rc; + }; + struct tpm_2hash_ext_log args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 8, 2); + args.method = (grub_ieee1275_cell_t) "2hash-ext-log"; + args.ihandle = grub_ieee1275_tpm_ihandle; + args.pcrindex = pcrindex; + args.eventtype = eventtype; + args.description = (grub_ieee1275_cell_t) description; + args.description_size = description_size; + args.buf = (grub_ieee1275_cell_t) buf; + args.size = (grub_ieee1275_cell_t) size; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return grub_error (GRUB_ERR_BAD_DEVICE, "2HASH-EXT-LOG failed: Firmware is likely too old.\n"); + + /* + * catch_result is set if firmware does not support 2hash-ext-log + * rc is GRUB_IEEE1275_CELL_FALSE (0) on failure + */ + if ((args.catch_result) || args.rc == GRUB_IEEE1275_CELL_FALSE) + return grub_error (GRUB_ERR_BAD_DEVICE, "2HASH-EXT-LOG failed: Firmware is likely too old.\n"); + + return GRUB_ERR_NONE; +} + grub_err_t grub_tcg2_get_max_output_size (grub_size_t *size) { @@ -155,3 +201,22 @@ grub_tcg2_submit_command (grub_size_t input_size, return GRUB_ERR_NONE; } + +grub_err_t +grub_tcg2_cap_pcr (grub_uint8_t pcr) +{ + char separator[4] = {0}; + static int error_displayed = 0; + grub_err_t err; + + err = grub_ieee1275_ibmvtpm_2hash_ext_log (pcr, GRUB_EV_SEPARATOR, + separator, sizeof (separator), + separator, sizeof (separator)); + if (err != GRUB_ERR_NONE && !error_displayed) + { + error_displayed++; + return err; + } + + return GRUB_ERR_NONE; +} diff --git a/include/grub/ieee1275/tpm.h b/include/grub/ieee1275/tpm.h index fe5cb4713..c34e8edc0 100644 --- a/include/grub/ieee1275/tpm.h +++ b/include/grub/ieee1275/tpm.h @@ -27,4 +27,9 @@ extern grub_ieee1275_ihandle_t grub_ieee1275_tpm_ihandle; extern grub_err_t grub_ieee1275_tpm_init (void); +extern grub_err_t grub_ieee1275_ibmvtpm_2hash_ext_log (grub_uint8_t pcrindex, + grub_uint32_t eventtype, + const char *description, + grub_size_t description_size, + void *buf, grub_size_t size); #endif From ae7a399005e055165dbc30b0fa53f3e86df2a882 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 3 Oct 2025 11:22:06 +0800 Subject: [PATCH 065/208] tss2: Implement grub_tcg2_cap_pcr() for emu Since there is no system firmware for grub-emu, the TPM2_PCR_Event command becomes the only choice to implement grub_tcg2_cap_pcr(). Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/lib/tss2/tcg2_emu.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/grub-core/lib/tss2/tcg2_emu.c b/grub-core/lib/tss2/tcg2_emu.c index cab930d2b..3713190ac 100644 --- a/grub-core/lib/tss2/tcg2_emu.c +++ b/grub-core/lib/tss2/tcg2_emu.c @@ -22,6 +22,7 @@ #include #include +#include #include grub_err_t @@ -47,3 +48,22 @@ grub_tcg2_submit_command (grub_size_t input_size, grub_uint8_t *input, return GRUB_ERR_NONE; } + +grub_err_t +grub_tcg2_cap_pcr (grub_uint8_t pcr) +{ + TPMS_AUTH_COMMAND_t authCmd = { + .sessionHandle = TPM_RS_PW, + }; + TPM2B_EVENT_t data = { + .size = 4, + }; + TPM_RC_t rc; + + /* Submit an EV_SEPARATOR event, i.e. an event with 4 zero-bytes */ + rc = grub_tpm2_pcr_event (pcr, &authCmd, &data, NULL, NULL); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot cap PCR %u"), pcr); + + return GRUB_ERR_NONE; +} From afddba0127d187a5f21f0035aa4dd41af0687bb6 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 3 Oct 2025 11:22:07 +0800 Subject: [PATCH 066/208] tpm2_key_protector: Support PCR capping To prevent a sealed key from being unsealed again, a common and straightforward method is to "cap" the key by extending the associated PCRs. When the PCRs associated with the sealed key are extended, TPM will be unable to unseal the key, as the PCR values required for unsealing no longer match, effectively rendering the key unusable until the next system boot or a state where the PCRs are reset to their expected values. To cap a specific set of PCRs, simply append the argument '-c pcr_list' to the tpm2_key_protector command. Upon successfully unsealing the key, the TPM2 key protector will then invoke tpm2_protector_cap_pcrs(). This function extends the selected PCRs with an EV_SEPARATOR event, effectively "capping" them. Consequently, the associated key cannot be unsealed in any subsequent attempts until these PCRs are reset to their original, pre-capped state, typically occurring upon the next system boot. Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- docs/grub.texi | 20 ++++++- .../commands/tpm2_key_protector/module.c | 56 ++++++++++++++++++- grub-core/normal/main.c | 2 +- 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index d697010ce..25e602a56 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -8458,7 +8458,7 @@ either @var{expression1} or @var{expression2} is true @node tpm2_key_protector_init @subsection tpm2_key_protector_init -@deffn Command tpm2_key_protector_init [@option{--mode} | @option{-m} mode] | [@option{--pcrs} | @option{-p} pcrlist] | [@option{--bank} | @option{-b} pcrbank] | [ [@option{--tpm2key} | @option{-T} tpm2key_file] | [@option{--keyfile} | @option{-k} keyfile] ] | [@option{--srk} | @option{-s} handle] | [@option{--asymmetric} | @option{-a} srk_type] | [@option{--nvindex} | @option{-n} nv_index] +@deffn Command tpm2_key_protector_init [@option{--mode} | @option{-m} mode] | [@option{--pcrs} | @option{-p} pcrlist] | [@option{--bank} | @option{-b} pcrbank] | [@option{--cap-pcrs} | @option{-c} pcrlist] | [ [@option{--tpm2key} | @option{-T} tpm2key_file] | [@option{--keyfile} | @option{-k} keyfile] ] | [@option{--srk} | @option{-s} handle] | [@option{--asymmetric} | @option{-a} srk_type] | [@option{--nvindex} | @option{-n} nv_index] Initialize the TPM2 key protector to unseal the key for the @command{cryptomount} (@pxref{cryptomount}) command. There are two supported modes, SRK(@kbd{srk}) and NV index(@kbd{nv}), to be specified by the option @@ -8473,6 +8473,24 @@ bank that the key is sealed with. The PCR list is a comma-separated list, e.g., bank is chosen by selecting a hash algorithm. The current supported PCR banks are SHA1, SHA256, SHA384, and SHA512, and the default is SHA256. +The @option{-c} option is introduced to enable the "capping" of a specified list of +PCRs. This feature addresses scenarios where a user wants to ensure a sealed key +cannot be unsealed again after its initial use. When the @option{-c} option is +employed, and the key is successfully unsealed, the TPM2 key protector automatically +extends the selected PCRs with an EV_SEPARATOR event. This action cryptographically +alters the PCR values, thereby preventing the associated key from being unsealed in +any subsequent attempts until those specific PCRs are reset to their original state, +which typically occurs during a system reboot. In general, it is sufficient to +extend one associated PCR to cap the key. + +It's noteworthy that a key sealed against PCR 8 naturally incorporates a "capping" +behavior, even without explicitly using a @option{-c} option. This is because GRUB +measures all commands into PCR 8, including those from configuration files. As a +result, the value of PCR 8 changes with virtually every command execution during +the boot process. Consequently, a key sealed against PCR 8 can only be unsealed +once in a given boot session, as any subsequent GRUB command will alter PCR 8, +invalidating the unsealing policy and effectively "capping" the key. + Some options are only available for the specific mode. The SRK-specific options are @option{-T}, @option{-k}, @option{-a}, and @option{-s}. On the other hand, the NV index-specific option is @option{-n}. diff --git a/grub-core/commands/tpm2_key_protector/module.c b/grub-core/commands/tpm2_key_protector/module.c index b84c2234f..1226b65e0 100644 --- a/grub-core/commands/tpm2_key_protector/module.c +++ b/grub-core/commands/tpm2_key_protector/module.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "tpm2_args.h" #include "tpm2.h" @@ -47,6 +48,7 @@ typedef enum tpm2_protector_options OPTION_MODE, OPTION_PCRS, OPTION_BANK, + OPTION_CAPPCRS, OPTION_TPM2KEY, OPTION_KEYFILE, OPTION_SRK, @@ -61,6 +63,8 @@ typedef struct tpm2_protector_context grub_uint8_t pcr_count; grub_srk_type_t srk_type; TPM_ALG_ID_t bank; + grub_uint8_t cap_pcrs[TPM_MAX_PCRS]; + grub_uint8_t cap_pcr_count; const char *tpm2key; const char *keyfile; TPM_HANDLE_t srk; @@ -100,6 +104,16 @@ static const struct grub_arg_option tpm2_protector_init_cmd_options[] = N_("Bank of PCRs used to authorize key release: " "SHA1, SHA256, SHA384 or SHA512. (default: SHA256)"), }, + { + .longarg = "cap-pcrs", + .shortarg = 'c', + .flags = 0, + .arg = NULL, + .type = ARG_TYPE_STRING, + .doc = + N_("Comma-separated list of PCRs to be capped after key release " + "e.g., '7,11'."), + }, /* SRK-mode options */ { .longarg = "tpm2key", @@ -1212,19 +1226,45 @@ tpm2_protector_nv_recover (const tpm2_protector_context_t *ctx, return err; } +static grub_err_t +tpm2_protector_cap_pcrs (const tpm2_protector_context_t *ctx) +{ + grub_uint8_t i; + grub_err_t err; + + for (i = 0; i < ctx->cap_pcr_count; i++) + { + err = grub_tcg2_cap_pcr (ctx->cap_pcrs[i]); + if (err != GRUB_ERR_NONE) + return err; + } + + return GRUB_ERR_NONE; +} + static grub_err_t tpm2_protector_recover (const tpm2_protector_context_t *ctx, grub_uint8_t **key, grub_size_t *key_size) { + grub_err_t err; + switch (ctx->mode) { case TPM2_PROTECTOR_MODE_SRK: - return tpm2_protector_srk_recover (ctx, key, key_size); + err = tpm2_protector_srk_recover (ctx, key, key_size); + break; case TPM2_PROTECTOR_MODE_NV: - return tpm2_protector_nv_recover (ctx, key, key_size); + err = tpm2_protector_nv_recover (ctx, key, key_size); + break; default: - return GRUB_ERR_BAD_ARGUMENT; + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Unknown Mode")); } + + /* Cap the selected PCRs when the key is unsealed successfully */ + if (ctx->cap_pcr_count > 0 && err == GRUB_ERR_NONE) + err = tpm2_protector_cap_pcrs (ctx); + + return err; } static grub_err_t @@ -1364,6 +1404,15 @@ tpm2_protector_init_cmd_handler (grub_extcmd_context_t ctxt, int argc, return err; } + if (state[OPTION_CAPPCRS].set) /* cap-pcrs */ + { + err = grub_tpm2_protector_parse_pcrs (state[OPTION_CAPPCRS].arg, + tpm2_protector_ctx.cap_pcrs, + &tpm2_protector_ctx.cap_pcr_count); + if (err != GRUB_ERR_NONE) + return err; + } + if (state[OPTION_TPM2KEY].set) /* tpm2key */ { err = tpm2_protector_parse_file (state[OPTION_TPM2KEY].arg, @@ -1465,6 +1514,7 @@ GRUB_MOD_INIT (tpm2_key_protector) N_("[-m mode] " "[-p pcr_list] " "[-b pcr_bank] " + "[-c pcr_list] " "[-T tpm2_key_file_path] " "[-k sealed_key_file_path] " "[-s srk_handle] " diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index a9e7a09d1..01b79ac32 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -518,7 +518,7 @@ static const char *features[] = { "feature_default_font_path", "feature_all_video_module", "feature_menuentry_id", "feature_menuentry_options", "feature_200_final", "feature_nativedisk_cmd", "feature_timeout_style", - "feature_search_cryptodisk_only" + "feature_search_cryptodisk_only", "feature_tpm2_cap_pcrs" }; GRUB_MOD_INIT(normal) From 21cdcb125c84840dba2aa14e696da90495e24732 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 3 Oct 2025 11:22:08 +0800 Subject: [PATCH 067/208] tests/tpm2_key_protector_test: Add a test for PCR Capping A test is introduced to cap PCR 1 and track the PCR 1 value before and after key unsealing. Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- tests/tpm2_key_protector_test.in | 65 ++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/tpm2_key_protector_test.in b/tests/tpm2_key_protector_test.in index 1d80d5d26..5dd86d6ee 100644 --- a/tests/tpm2_key_protector_test.in +++ b/tests/tpm2_key_protector_test.in @@ -304,6 +304,58 @@ EOF fi } +tpm2_seal_unseal_cap() { + pcr_bank="sha256" + + original_pcr1="$(tpm2_pcrread ${pcr_bank}:1) | tail -1 | cut -d' ' -f7" + + grub_cfg=${tpm2testdir}/testcase.cfg + + # Seal the password with grub-protect + grub-protect \ + --tpm2-device="${tpm2dev}" \ + --action=add \ + --protector=tpm2 \ + --tpm2key \ + --tpm2-bank="${pcr_bank}" \ + --tpm2-pcrs=0,1 \ + --tpm2-keyfile="${lukskeyfile}" \ + --tpm2-outfile="${sealedkey}" || ret=$? + if [ "${ret}" -ne 0 ]; then + echo "Failed to seal the secret key: ${ret}" >&2 + return 99 + fi + + # Write the TPM unsealing script and cap PCR 1 + cat > "${grub_cfg}" < "${testoutput}" || ret=$? + + if [ "${ret}" -eq 0 ]; then + if ! grep -q "^${vtext}$" "${testoutput}"; then + echo "error: test not verified [`cat ${testoutput}`]" >&2 + return 1 + fi + else + echo "grub-emu exited with error: ${ret}" >&2 + return 99 + fi + + capped_pcr1="$(tpm2_pcrread ${pcr_bank}:1) | tail -1 | cut -d' ' -f7" + + if [ "${original_pcr1}" = "${capped_pcr1}" ]; then + echo "error: PCR 1 not capped" >&2 + return 1 + fi +} + # Testcases for SRK mode declare -a srktests=() srktests+=("default transient no_fallback_srk sha256") @@ -357,4 +409,17 @@ for i in "${!nvtests[@]}"; do fi done +# Testcase for PCR Capping +tpm2_seal_unseal_cap || ret=$? +if [ "${ret}" -eq 0 ]; then + echo "TPM2 [PCR Capping]: PASS" +elif [ "${ret}" -eq 1 ]; then + echo "TPM2 [PCR Capping]: FAIL" + ret=0 + exit_status=1 +else + echo "Unexpected failure [PCR Capping]" >&2 + exit ${ret} +fi + exit ${exit_status} From 6b5c671d35b1b84468da1fef2b9bd8e05682f7e6 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Thu, 9 Oct 2025 14:49:39 +0530 Subject: [PATCH 068/208] commands/menuentry: Fix for out of bound access A menu entry with an empty title leads to an out-of-bounds access at "ch = src[len - 1]", i.e., "src" is empty and "len" is zero. So, fixing this by checking the menu entry title length and throwing an error if the length is zero. Signed-off-by: Sridhar Markonda Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/commands/menuentry.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c index 09749c415..5e1318f17 100644 --- a/grub-core/commands/menuentry.c +++ b/grub-core/commands/menuentry.c @@ -266,6 +266,9 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) if (! argc) return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing arguments"); + if (! grub_strlen (args[argc - 1])) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "menuentry title is missing"); + if (ctxt->state[3].set && ctxt->script) return grub_error (GRUB_ERR_BAD_ARGUMENT, "multiple menuentry definitions"); From 5b81f490c421a6a17df591c0e02b7313cfe1bbf6 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 15 Oct 2025 11:00:16 +0800 Subject: [PATCH 069/208] util/import_gcry: Import kdf.c for Argon2 The import_gcry.py script now imports kdf.c from libgcrypt. To isolate the Argon2 implementation, all unrelated functions have been removed. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- util/import_gcry.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/util/import_gcry.py b/util/import_gcry.py index 1e23ee760..b2131643b 100644 --- a/util/import_gcry.py +++ b/util/import_gcry.py @@ -145,8 +145,8 @@ with codecs.open (os.path.join (cipher_dir_out, "crypto.lst"), "w", "utf-8") as if re.match (r"(Makefile\.am|primegen\.c|cipher\.c|cipher-.*\.c|mac-.*\.c|mac\.c|pubkey\.c)$", cipher_file): chlog = "%s%s: Removed\n" % (chlog, chlognew) continue - # TODO: Support KDF - if re.match (r"(kdf\.c|scrypt\.c)$", cipher_file): + # TODO: Support scrypt KDF + if re.match (r"(scrypt\.c)$", cipher_file): chlog = "%s%s: Removed\n" % (chlog, chlognew) continue # TODO: Support chacha20 and poly1305 @@ -313,6 +313,17 @@ with codecs.open (os.path.join (cipher_dir_out, "crypto.lst"), "w", "utf-8") as chlognew = "%s %s" % (chlognew, chmsg) nch = True continue + elif re.match ("_gcry_kdf_selftest|check_one|_gcry_kdf_pkdf2|_gcry_kdf_derive|openpgp_s2k|ballon_context_size|balloon_*|prng_aes_*|onestep_kdf_*|hkdf_*|x963_kdf_*", line) is not None and cipher_file == "kdf.c": + # TODO Support other KDFs + skip = 1 + fname = re.match ("[a-zA-Z0-9_]*", line).group () + chmsg = "(%s): Removed." % fname + if nch: + chlognew = "%s\n %s" % (chlognew, chmsg) + else: + chlognew = "%s %s" % (chlognew, chmsg) + nch = True + continue else: fw.write (holdline) m = re.match ("# *include <(.*)>", line) @@ -439,7 +450,7 @@ with codecs.open (os.path.join (cipher_dir_out, "crypto.lst"), "w", "utf-8") as nch = True continue - m = re.match (r"((static )?const char( |)\*|static const gcry_md_spec_t \*|(static )?gpg_err_code_t|gpg_error_t|void|(static )?int|(static )?unsigned int|(static )?gcry_err_code_t|static gcry_mpi_t|static void|void|static elliptic_curve_t) *$", line) + m = re.match (r"((static )?const char( |)\*|static const gcry_md_spec_t \*|(static )?gpg_err_code_t|gpg_error_t|void|(static )?int|(static )?unsigned int|(static )?gcry_err_code_t|static gcry_mpi_t|static void|void|static elliptic_curve_t|static u64|static size_t) *$", line) if not m is None: hold = True holdline = line @@ -575,6 +586,20 @@ with codecs.open (os.path.join (cipher_dir_out, "crypto.lst"), "w", "utf-8") as conf.write ("};\n\n") if nch: chlog = "%s%s\n" % (chlog, chlognew) + elif cipher_file == "kdf.c": + modfiles = ["kdf.c"] + if modname in extra_files: + modfiles += extra_files[modname] + conf.write ("module = {\n") + conf.write (" name = %s;\n" % modname) + for src in modfiles: + conf.write (" common = lib/libgcrypt-grub/cipher/%s;\n" % src) + confutil.write (" common = grub-core/lib/libgcrypt-grub/cipher/%s;\n" % src) + conf.write (" cflags = '$(CFLAGS_GCRY)';\n") + conf.write (" cppflags = '$(CPPFLAGS_GCRY)';\n") + conf.write ("};\n\n") + if nch: + chlog = "%s%s\n" % (chlog, chlognew) elif isc and cipher_file not in extra_files_list: print ("WARNING: C file isn't a module: %s" % cipher_file) os.remove (outfile) From bc94dfd5416407acab367217a415953cdde37207 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 15 Oct 2025 11:00:17 +0800 Subject: [PATCH 070/208] crypto: Update crypto.h for libgcrypt KDF functions This commit introduces the necessary changes to crypto.h in preparation for implementing Argon2 support via the generic KDF functions, _gcry_kdf_*(): - add new GPG error types required by kdf.c, - declare _gcry_digest_spec_blake2b_512 to enable BLAKE2b-512 digest calculations, - define the gcrypt KDF algorithm IDs for Argon2, - add the prototypes of _gcry_kdf_*() functions. Signed-off-by: Gary Lin Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- include/grub/crypto.h | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/include/grub/crypto.h b/include/grub/crypto.h index e4e2bd1a7..6758bae9a 100644 --- a/include/grub/crypto.h +++ b/include/grub/crypto.h @@ -34,6 +34,7 @@ typedef enum GPG_ERR_BAD_MPI, GPG_ERR_BAD_SECKEY, GPG_ERR_BAD_SIGNATURE, + GPG_ERR_CANCELED, GPG_ERR_CIPHER_ALGO, GPG_ERR_CONFLICT, GPG_ERR_DECRYPT_FAILED, @@ -48,6 +49,7 @@ typedef enum GPG_ERR_INV_OP, GPG_ERR_INV_SEXP, GPG_ERR_INV_VALUE, + GPG_ERR_MAC_ALGO, GPG_ERR_MISSING_VALUE, GPG_ERR_NO_ENCRYPTION_SCHEME, GPG_ERR_NO_OBJ, @@ -59,7 +61,9 @@ typedef enum GPG_ERR_PUBKEY_ALGO, GPG_ERR_SELFTEST_FAILED, GPG_ERR_TOO_SHORT, + GPG_ERR_UNKNOWN_ALGORITHM, GPG_ERR_UNSUPPORTED, + GPG_ERR_UNSUPPORTED_ALGORITHM, GPG_ERR_WEAK_KEY, GPG_ERR_WRONG_KEY_USAGE, GPG_ERR_WRONG_PUBKEY_ALGO, @@ -521,6 +525,7 @@ extern gcry_md_spec_t _gcry_digest_spec_sha256; extern gcry_md_spec_t _gcry_digest_spec_sha384; extern gcry_md_spec_t _gcry_digest_spec_sha512; extern gcry_md_spec_t _gcry_digest_spec_crc32; +extern gcry_md_spec_t _gcry_digest_spec_blake2b_512; extern gcry_cipher_spec_t _gcry_cipher_spec_aes; #define GRUB_MD_MD5 ((const gcry_md_spec_t *) &_gcry_digest_spec_md5) #define GRUB_MD_SHA1 ((const gcry_md_spec_t *) &_gcry_digest_spec_sha1) @@ -529,6 +534,41 @@ extern gcry_cipher_spec_t _gcry_cipher_spec_aes; #define GRUB_MD_CRC32 ((const gcry_md_spec_t *) &_gcry_digest_spec_crc32) #define GRUB_CIPHER_AES ((const gcry_cipher_spec_t *) &_gcry_cipher_spec_aes) +/* Algorithm IDs for the KDFs. */ +enum grub_gcry_kdf_algos + { + GRUB_GCRY_KDF_NONE = 0, + GRUB_GCRY_KDF_ARGON2 = 64, + }; + +enum grub_gcry_kdf_subalgo_argon2 + { + GRUB_GCRY_KDF_ARGON2D = 0, + GRUB_GCRY_KDF_ARGON2I = 1, + GRUB_GCRY_KDF_ARGON2ID = 2 + }; + +typedef struct gcry_kdf_handle *gcry_kdf_hd_t; +struct gcry_kdf_handle; +struct gcry_kdf_thread_ops; + +gpg_err_code_t +_gcry_kdf_open (gcry_kdf_hd_t *hd, int algo, int subalgo, + const unsigned long *param, unsigned int paramlen, + const void *input, grub_size_t inputlen, + const void *salt, grub_size_t saltlen, + const void *key, grub_size_t keylen, + const void *ad, grub_size_t adlen); + +gpg_err_code_t +_gcry_kdf_compute (gcry_kdf_hd_t h, const struct gcry_kdf_thread_ops *ops); + +gpg_err_code_t +_gcry_kdf_final (gcry_kdf_hd_t h, grub_size_t resultlen, void *result); + +void +_gcry_kdf_close (gcry_kdf_hd_t h); + /* Implement PKCS#5 PBKDF2 as per RFC 2898. The PRF to use is HMAC variant of digest supplied by MD. Inputs are the password P of length PLEN, the salt S of length SLEN, the iteration counter C (> 0), and the From 0c06a454fce726179bbd0199cfd3e1a1de2be707 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 15 Oct 2025 11:00:18 +0800 Subject: [PATCH 071/208] libgcrypt/kdf: Implement hash_buffers() for BLAKE2b-512 The hash_buffers() functions are disabled in GRUB by default but the Argon2 implementation requires hash_buffers() for BLAKE2b-512. This commit implements argon2_blake2b_512_hash_buffers() as the replacement of _gcry_digest_spec_blake2b_512.hash_buffers(). Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- conf/Makefile.extra-dist | 1 + .../09-blake2b-hash-buffers.patch | 63 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 grub-core/lib/libgcrypt-patches/09-blake2b-hash-buffers.patch diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index 10cbd4fa4..6da09b6d9 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -43,6 +43,7 @@ EXTRA_DIST += grub-core/lib/libgcrypt-patches/05_disable_rsa_shake.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/06_blake.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/07_disable_64div.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/08_sexp_leak.patch +EXTRA_DIST += grub-core/lib/libgcrypt-patches/09-blake2b-hash-buffers.patch EXTRA_DIST += grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch EXTRA_DIST += grub-core/lib/libtasn1-patches/0002-libtasn1-replace-strcat-with-strcpy-in-_asn1_str_cat.patch diff --git a/grub-core/lib/libgcrypt-patches/09-blake2b-hash-buffers.patch b/grub-core/lib/libgcrypt-patches/09-blake2b-hash-buffers.patch new file mode 100644 index 000000000..fd4bae571 --- /dev/null +++ b/grub-core/lib/libgcrypt-patches/09-blake2b-hash-buffers.patch @@ -0,0 +1,63 @@ +From 42e9975171439e2e9713e122cb0e74174f057e98 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Mon, 25 Aug 2025 15:54:24 +0800 +Subject: [PATCH 1/4] libgcrypt/kdf: Implement blake2b_512.hash_buffers() + +Add argon2_blake2b_512_hash_buffers() as the replacement of +_gcry_digest_spec_blake2b_512.hash_buffers(). + +Signed-off-by: Gary Lin +--- + grub-core/lib/libgcrypt-grub/cipher/kdf.c | 25 ++++++++++++++++++++++- + 1 file changed, 24 insertions(+), 1 deletion(-) + +diff --git a/grub-core/lib/libgcrypt-grub/cipher/kdf.c b/grub-core/lib/libgcrypt-grub/cipher/kdf.c +index 0689f88b1..08e3ef658 100644 +--- a/grub-core/lib/libgcrypt-grub/cipher/kdf.c ++++ b/grub-core/lib/libgcrypt-grub/cipher/kdf.c +@@ -129,10 +129,31 @@ beswap64_block (u64 *dst) + #endif + } + ++/* Implementation of _gcry_blake2b_512_hash_buffers */ ++static gcry_err_code_t ++argon2_blake2b_512_hash_buffers (void *outbuf, const gcry_buffer_t *iov, int iovcnt) ++{ ++ void *hd; ++ ++ hd = xtrymalloc (_gcry_digest_spec_blake2b_512.contextsize); ++ if (!hd) ++ return GPG_ERR_OUT_OF_MEMORY; ++ ++ _gcry_digest_spec_blake2b_512.init (hd, 0); ++ for (;iovcnt > 0; iov++, iovcnt--) ++ _gcry_digest_spec_blake2b_512.write (hd, (const char*)iov[0].data + iov[0].off, iov[0].len); ++ _gcry_digest_spec_blake2b_512.final (hd); ++ grub_memcpy (outbuf, _gcry_digest_spec_blake2b_512.read (hd), 512 / 8); ++ ++ xfree (hd); ++ ++ return GPG_ERR_NO_ERROR; ++} + + static gpg_err_code_t + argon2_fill_first_blocks (argon2_ctx_t a) + { ++ gpg_err_code_t err; + unsigned char h0_01_i[72]; + unsigned char buf[10][4]; + gcry_buffer_t iov[8]; +@@ -195,7 +216,9 @@ argon2_fill_first_blocks (argon2_ctx_t a) + iov_count++; + } + +- _gcry_digest_spec_blake2b_512.hash_buffers (h0_01_i, 64, iov, iov_count); ++ err = argon2_blake2b_512_hash_buffers (h0_01_i, iov, iov_count); ++ if (err != GPG_ERR_NO_ERROR) ++ return err; + + for (i = 0; i < a->lanes; i++) + { +-- +2.51.0 + From 1ff72064178794b13ad3bf690bfa37e8fc76e2b9 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 15 Oct 2025 11:00:19 +0800 Subject: [PATCH 072/208] libgcrypt/kdf: Get rid of gpg_err_code_from_errno() gpg_err_code_from_errno() requires libgcrypt_wrap/mem.c which is not in Makefile.utilgcry.def. This commit replaces gpg_err_code_from_errno() with GPG_ERR_* to avoid the build errors. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- conf/Makefile.extra-dist | 1 + .../10-kdf-use-GPG-errs.patch | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 grub-core/lib/libgcrypt-patches/10-kdf-use-GPG-errs.patch diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index 6da09b6d9..e1e569de0 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -44,6 +44,7 @@ EXTRA_DIST += grub-core/lib/libgcrypt-patches/06_blake.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/07_disable_64div.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/08_sexp_leak.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/09-blake2b-hash-buffers.patch +EXTRA_DIST += grub-core/lib/libgcrypt-patches/10-kdf-use-GPG-errs.patch EXTRA_DIST += grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch EXTRA_DIST += grub-core/lib/libtasn1-patches/0002-libtasn1-replace-strcat-with-strcpy-in-_asn1_str_cat.patch diff --git a/grub-core/lib/libgcrypt-patches/10-kdf-use-GPG-errs.patch b/grub-core/lib/libgcrypt-patches/10-kdf-use-GPG-errs.patch new file mode 100644 index 000000000..53ca52d53 --- /dev/null +++ b/grub-core/lib/libgcrypt-patches/10-kdf-use-GPG-errs.patch @@ -0,0 +1,48 @@ +From 89f793515d927d8f7099b61d0b7b200611e56acd Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Mon, 25 Aug 2025 15:56:03 +0800 +Subject: [PATCH 2/4] libgcrypt/kdf: Get rid of gpg_err_code_from_errno() + +gpg_err_code_from_errno() requires libgcrypt_wrap/mem.c which is not in +Makefile.utilgcry.def. This commit replaces gpg_err_code_from_errno() +with GPG_ERR_* to avoid the build errors. + +Signed-off-by: Gary Lin +--- + grub-core/lib/libgcrypt-grub/cipher/kdf.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/grub-core/lib/libgcrypt-grub/cipher/kdf.c b/grub-core/lib/libgcrypt-grub/cipher/kdf.c +index 08e3ef658..6e0351d8a 100644 +--- a/grub-core/lib/libgcrypt-grub/cipher/kdf.c ++++ b/grub-core/lib/libgcrypt-grub/cipher/kdf.c +@@ -265,7 +265,7 @@ argon2_init (argon2_ctx_t a, unsigned int parallelism, + block = xtrymalloc (1024 * memory_blocks); + if (!block) + { +- ec = gpg_err_code_from_errno (errno); ++ ec = GPG_ERR_OUT_OF_MEMORY; + return ec; + } + memset (block, 0, 1024 * memory_blocks); +@@ -273,7 +273,7 @@ argon2_init (argon2_ctx_t a, unsigned int parallelism, + thread_data = xtrymalloc (a->lanes * sizeof (struct argon2_thread_data)); + if (!thread_data) + { +- ec = gpg_err_code_from_errno (errno); ++ ec = GPG_ERR_OUT_OF_MEMORY; + xfree (block); + return ec; + } +@@ -624,7 +624,7 @@ argon2_open (gcry_kdf_hd_t *hd, int subalgo, + n = offsetof (struct argon2_context, out) + taglen; + a = xtrymalloc (n); + if (!a) +- return gpg_err_code_from_errno (errno); ++ return GPG_ERR_OUT_OF_MEMORY; + + a->algo = GCRY_KDF_ARGON2; + a->hash_type = hash_type; +-- +2.51.0 + From 93544861bc9cab96e7515b44a299a4010407733a Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 15 Oct 2025 11:00:20 +0800 Subject: [PATCH 073/208] libgcrypt/kdf: Remove unsupported KDFs Clean up _gcry_kdf_*() to remove unsupported KDFs. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- conf/Makefile.extra-dist | 1 + .../11-kdf-remove-unsupported-kdfs.patch | 166 ++++++++++++++++++ 2 files changed, 167 insertions(+) create mode 100644 grub-core/lib/libgcrypt-patches/11-kdf-remove-unsupported-kdfs.patch diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index e1e569de0..bc6768549 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -45,6 +45,7 @@ EXTRA_DIST += grub-core/lib/libgcrypt-patches/07_disable_64div.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/08_sexp_leak.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/09-blake2b-hash-buffers.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/10-kdf-use-GPG-errs.patch +EXTRA_DIST += grub-core/lib/libgcrypt-patches/11-kdf-remove-unsupported-kdfs.patch EXTRA_DIST += grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch EXTRA_DIST += grub-core/lib/libtasn1-patches/0002-libtasn1-replace-strcat-with-strcpy-in-_asn1_str_cat.patch diff --git a/grub-core/lib/libgcrypt-patches/11-kdf-remove-unsupported-kdfs.patch b/grub-core/lib/libgcrypt-patches/11-kdf-remove-unsupported-kdfs.patch new file mode 100644 index 000000000..44ef3dec2 --- /dev/null +++ b/grub-core/lib/libgcrypt-patches/11-kdf-remove-unsupported-kdfs.patch @@ -0,0 +1,166 @@ +From fc9c57f54fd28685f7df79e53078e1dc9e44f964 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Mon, 25 Aug 2025 16:00:17 +0800 +Subject: [PATCH 3/4] libgcrypt/kdf: Remove unsupported KDFs + +Clean up _gcry_kdf_*() to remove unsupported KDFs. + +Signed-off-by: Gary Lin +--- + grub-core/lib/libgcrypt-grub/cipher/kdf.c | 119 ---------------------- + 1 file changed, 119 deletions(-) + +diff --git a/grub-core/lib/libgcrypt-grub/cipher/kdf.c b/grub-core/lib/libgcrypt-grub/cipher/kdf.c +index 6e0351d8a..c51a70eff 100644 +--- a/grub-core/lib/libgcrypt-grub/cipher/kdf.c ++++ b/grub-core/lib/libgcrypt-grub/cipher/kdf.c +@@ -821,64 +821,6 @@ _gcry_kdf_open (gcry_kdf_hd_t *hd, int algo, int subalgo, + key, keylen, ad, adlen); + break; + +- case GCRY_KDF_BALLOON: +- if (!inputlen || !saltlen || keylen || adlen) +- ec = GPG_ERR_INV_VALUE; +- else +- { +- (void)key; +- (void)ad; +- ec = balloon_open (hd, subalgo, param, paramlen, +- input, inputlen, salt, saltlen); +- } +- break; +- +- case GCRY_KDF_ONESTEP_KDF: +- if (!inputlen || !paramlen || !adlen) +- ec = GPG_ERR_INV_VALUE; +- else +- { +- (void)salt; +- (void)key; +- ec = onestep_kdf_open (hd, subalgo, param, paramlen, +- input, inputlen, ad, adlen); +- } +- break; +- +- case GCRY_KDF_ONESTEP_KDF_MAC: +- if (!inputlen || !paramlen || !keylen || !adlen) +- ec = GPG_ERR_INV_VALUE; +- else +- { +- (void)salt; +- ec = onestep_kdf_mac_open (hd, subalgo, param, paramlen, +- input, inputlen, key, keylen, ad, adlen); +- } +- break; +- +- case GCRY_KDF_HKDF: +- if (!inputlen || !paramlen) +- ec = GPG_ERR_INV_VALUE; +- else +- { +- (void)salt; +- ec = hkdf_open (hd, subalgo, param, paramlen, +- input, inputlen, key, keylen, ad, adlen); +- } +- break; +- +- case GCRY_KDF_X963_KDF: +- if (!inputlen || !paramlen) +- ec = GPG_ERR_INV_VALUE; +- else +- { +- (void)salt; +- (void)key; +- ec = x963_kdf_open (hd, subalgo, param, paramlen, +- input, inputlen, ad, adlen); +- } +- break; +- + default: + ec = GPG_ERR_UNKNOWN_ALGORITHM; + break; +@@ -898,26 +840,6 @@ _gcry_kdf_compute (gcry_kdf_hd_t h, const struct gcry_kdf_thread_ops *ops) + ec = argon2_compute ((argon2_ctx_t)(void *)h, ops); + break; + +- case GCRY_KDF_BALLOON: +- ec = balloon_compute_all ((balloon_ctx_t)(void *)h, ops); +- break; +- +- case GCRY_KDF_ONESTEP_KDF: +- ec = onestep_kdf_compute ((onestep_kdf_ctx_t)(void *)h, ops); +- break; +- +- case GCRY_KDF_ONESTEP_KDF_MAC: +- ec = onestep_kdf_mac_compute ((onestep_kdf_mac_ctx_t)(void *)h, ops); +- break; +- +- case GCRY_KDF_HKDF: +- ec = hkdf_compute ((hkdf_ctx_t)(void *)h, ops); +- break; +- +- case GCRY_KDF_X963_KDF: +- ec = x963_kdf_compute ((x963_kdf_ctx_t)(void *)h, ops); +- break; +- + default: + ec = GPG_ERR_UNKNOWN_ALGORITHM; + break; +@@ -938,27 +860,6 @@ _gcry_kdf_final (gcry_kdf_hd_t h, size_t resultlen, void *result) + ec = argon2_final ((argon2_ctx_t)(void *)h, resultlen, result); + break; + +- case GCRY_KDF_BALLOON: +- ec = balloon_final ((balloon_ctx_t)(void *)h, resultlen, result); +- break; +- +- case GCRY_KDF_ONESTEP_KDF: +- ec = onestep_kdf_final ((onestep_kdf_ctx_t)(void *)h, resultlen, result); +- break; +- +- case GCRY_KDF_ONESTEP_KDF_MAC: +- ec = onestep_kdf_mac_final ((onestep_kdf_mac_ctx_t)(void *)h, +- resultlen, result); +- break; +- +- case GCRY_KDF_HKDF: +- ec = hkdf_final ((hkdf_ctx_t)(void *)h, resultlen, result); +- break; +- +- case GCRY_KDF_X963_KDF: +- ec = x963_kdf_final ((x963_kdf_ctx_t)(void *)h, resultlen, result); +- break; +- + default: + ec = GPG_ERR_UNKNOWN_ALGORITHM; + break; +@@ -976,26 +877,6 @@ _gcry_kdf_close (gcry_kdf_hd_t h) + argon2_close ((argon2_ctx_t)(void *)h); + break; + +- case GCRY_KDF_BALLOON: +- balloon_close ((balloon_ctx_t)(void *)h); +- break; +- +- case GCRY_KDF_ONESTEP_KDF: +- onestep_kdf_close ((onestep_kdf_ctx_t)(void *)h); +- break; +- +- case GCRY_KDF_ONESTEP_KDF_MAC: +- onestep_kdf_mac_close ((onestep_kdf_mac_ctx_t)(void *)h); +- break; +- +- case GCRY_KDF_HKDF: +- hkdf_close ((hkdf_ctx_t)(void *)h); +- break; +- +- case GCRY_KDF_X963_KDF: +- x963_kdf_close ((x963_kdf_ctx_t)(void *)h); +- break; +- + default: + break; + } +-- +2.51.0 + From de201105d4f5183f895f5ee70b4e31a0076e3040 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 15 Oct 2025 11:00:21 +0800 Subject: [PATCH 074/208] libgcrypt/kdf: Fix 64-bit modulus on 32-bit platforms Use grub_divmod64() for the 64-bit modulus to prevent creation of special division calls such as __umoddi3() and __aeabi_uldivmod() on 32-bit platforms. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- conf/Makefile.extra-dist | 1 + .../12-kdf-use-grub_divmod64.patch | 79 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 grub-core/lib/libgcrypt-patches/12-kdf-use-grub_divmod64.patch diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index bc6768549..48c2b75f8 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -46,6 +46,7 @@ EXTRA_DIST += grub-core/lib/libgcrypt-patches/08_sexp_leak.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/09-blake2b-hash-buffers.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/10-kdf-use-GPG-errs.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/11-kdf-remove-unsupported-kdfs.patch +EXTRA_DIST += grub-core/lib/libgcrypt-patches/12-kdf-use-grub_divmod64.patch EXTRA_DIST += grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch EXTRA_DIST += grub-core/lib/libtasn1-patches/0002-libtasn1-replace-strcat-with-strcpy-in-_asn1_str_cat.patch diff --git a/grub-core/lib/libgcrypt-patches/12-kdf-use-grub_divmod64.patch b/grub-core/lib/libgcrypt-patches/12-kdf-use-grub_divmod64.patch new file mode 100644 index 000000000..644485ebf --- /dev/null +++ b/grub-core/lib/libgcrypt-patches/12-kdf-use-grub_divmod64.patch @@ -0,0 +1,79 @@ +From 990a5f7df076200aa031b1bcde6bc4b13d4f198e Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Mon, 25 Aug 2025 16:01:45 +0800 +Subject: [PATCH 4/4] libgcrypt/kdf: Fix 64-bit modulus on 32-bit platforms + +Use grub_divmod64() for the 64-bit modulus to prevent creation of +special division calls such as __umoddi3() and __aeabi_uldivmod() on +32-bit platforms. + +Signed-off-by: Gary Lin +--- + grub-core/lib/libgcrypt-grub/cipher/kdf.c | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +diff --git a/grub-core/lib/libgcrypt-grub/cipher/kdf.c b/grub-core/lib/libgcrypt-grub/cipher/kdf.c +index c51a70eff..f4bb51809 100644 +--- a/grub-core/lib/libgcrypt-grub/cipher/kdf.c ++++ b/grub-core/lib/libgcrypt-grub/cipher/kdf.c +@@ -375,6 +375,7 @@ index_alpha (argon2_ctx_t a, const struct argon2_thread_data *t, + u32 reference_area_size; + u64 relative_position; + u32 start_position; ++ u64 remainder; + + if (t->pass == 0) + { +@@ -411,7 +412,8 @@ index_alpha (argon2_ctx_t a, const struct argon2_thread_data *t, + ? 0 + : (t->slice + 1) * a->segment_length; + +- return (start_position + relative_position) % a->lane_length; ++ grub_divmod64 (start_position + relative_position, a->lane_length, &remainder); ++ return remainder; + } + + static void +@@ -425,6 +427,7 @@ argon2_compute_segment (void *priv) + u64 input_block[1024/sizeof (u64)]; + u64 address_block[1024/sizeof (u64)]; + u64 *random_block = NULL; ++ u64 remainder; + + if (a->hash_type == GCRY_KDF_ARGON2I + || (a->hash_type == GCRY_KDF_ARGON2ID && t->pass == 0 && t->slice < 2)) +@@ -449,7 +452,8 @@ argon2_compute_segment (void *priv) + i = 0; + + curr_offset = t->lane * a->lane_length + t->slice * a->segment_length + i; +- if ((curr_offset % a->lane_length)) ++ grub_divmod64 (curr_offset, a->lane_length, &remainder); ++ if (remainder) + prev_offset = curr_offset - 1; + else + prev_offset = curr_offset + a->lane_length - 1; +@@ -459,7 +463,8 @@ argon2_compute_segment (void *priv) + u64 *ref_block, *curr_block; + u64 rand64; + +- if ((curr_offset % a->lane_length) == 1) ++ grub_divmod64 (curr_offset, a->lane_length, &remainder); ++ if (remainder == 1) + prev_offset = curr_offset - 1; + + if (random_block) +@@ -475,7 +480,10 @@ argon2_compute_segment (void *priv) + if (t->pass == 0 && t->slice == 0) + ref_lane = t->lane; + else +- ref_lane = (rand64 >> 32) % a->lanes; ++ { ++ grub_divmod64 (rand64 >> 32, a->lanes, &remainder); ++ ref_lane = remainder; ++ } + + ref_index = index_alpha (a, t, i, (rand64 & 0xffffffff), + ref_lane == t->lane); +-- +2.51.0 + From 66b8718f93dc89d979b395367d337cba8d528fe6 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 15 Oct 2025 11:00:22 +0800 Subject: [PATCH 075/208] argon2: Introduce grub_crypto_argon2() This commit introduces grub_crypto_argon2() which leverages the _gcry_kdf_*() functions from libgcrypt to provide Argon2 support. Due to the dependency of the _gcry_kdf_*() functions, the order of "ldadd" entries have to be tweaked in Makefile.util.def so that the linker can discover these functions. Signed-off-by: Gary Lin Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- Makefile.util.def | 55 +++++++++++++++++++------------------ grub-core/Makefile.core.def | 5 ++++ grub-core/lib/argon2.c | 52 +++++++++++++++++++++++++++++++++++ include/grub/crypto.h | 9 ++++++ 4 files changed, 94 insertions(+), 27 deletions(-) create mode 100644 grub-core/lib/argon2.c diff --git a/Makefile.util.def b/Makefile.util.def index 038253b37..f8d4ae7d3 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -43,6 +43,7 @@ library = { common = grub-core/disk/key_protector.c; common = grub-core/disk/cryptodisk.c; common = grub-core/disk/AFSplitter.c; + common = grub-core/lib/argon2.c; common = grub-core/lib/pbkdf2.c; common = grub-core/commands/extcmd.c; common = grub-core/lib/arg.c; @@ -201,8 +202,8 @@ program = { extra_dist = util/grub-mkimagexx.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBLZMA)'; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; @@ -225,8 +226,8 @@ program = { cflags = '-I$(srcdir)/grub-core/lib/tss2 -I$(srcdir)/grub-core/commands/tpm2_key_protector'; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBTASN1)'; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; @@ -243,8 +244,8 @@ program = { common = grub-core/osdep/init.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -258,8 +259,8 @@ program = { common = grub-core/osdep/init.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -284,8 +285,8 @@ program = { ldadd = '$(LIBLZMA)'; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -300,8 +301,8 @@ program = { common = grub-core/osdep/init.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -323,8 +324,8 @@ program = { common = grub-core/osdep/init.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -341,8 +342,8 @@ program = { cflags = '$(FUSE_CFLAGS)'; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM) $(FUSE_LIBS)'; condition = COND_GRUB_MOUNT; @@ -359,8 +360,8 @@ program = { cppflags = '-DGRUB_MKFONT=1'; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(FREETYPE_LIBS)'; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; @@ -378,8 +379,8 @@ program = { common = grub-core/osdep/init.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -436,8 +437,8 @@ program = { common = grub-core/osdep/init.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -451,8 +452,8 @@ program = { common = grub-core/osdep/init.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -466,8 +467,8 @@ program = { common = grub-core/kern/emu/argp_common.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -605,8 +606,8 @@ program = { ldadd = '$(LIBLZMA)'; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; @@ -652,8 +653,8 @@ program = { ldadd = '$(LIBLZMA)'; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -691,8 +692,8 @@ program = { ldadd = '$(LIBLZMA)'; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -727,8 +728,8 @@ program = { ldadd = '$(LIBLZMA)'; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -1305,8 +1306,8 @@ program = { common = grub-core/kern/misc.c; common = grub-core/tests/lib/test.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -1320,8 +1321,8 @@ program = { common = grub-core/kern/misc.c; common = grub-core/tests/lib/test.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -1335,8 +1336,8 @@ program = { common = grub-core/kern/misc.c; common = grub-core/tests/lib/test.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -1351,8 +1352,8 @@ program = { common = grub-core/tests/lib/test.c; common = grub-core/lib/priority_queue.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; condition = COND_HAVE_CXX; @@ -1367,8 +1368,8 @@ program = { common = grub-core/kern/misc.c; common = grub-core/tests/lib/test.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -1382,8 +1383,8 @@ program = { common = grub-core/osdep/init.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -1400,8 +1401,8 @@ program = { common = grub-core/kern/emu/argp_common.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -1416,8 +1417,8 @@ program = { common = grub-core/osdep/init.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -1434,8 +1435,8 @@ program = { common = grub-core/osdep/init.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -1463,8 +1464,8 @@ program = { common = grub-core/osdep/init.c; ldadd = libgrubmods.a; - ldadd = libgrubgcry.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index a729cb6a8..b378b9b60 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1729,6 +1729,11 @@ module = { common = lib/pbkdf2.c; }; +module = { + name = argon2; + common = lib/argon2.c; +}; + module = { name = relocator; common = lib/relocator.c; diff --git a/grub-core/lib/argon2.c b/grub-core/lib/argon2.c new file mode 100644 index 000000000..12ad7ad1c --- /dev/null +++ b/grub-core/lib/argon2.c @@ -0,0 +1,52 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2025 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +gcry_err_code_t +grub_crypto_argon2 (int subalgo, + const unsigned long *param, unsigned int paramlen, + const void *password, grub_size_t passwordlen, + const void *salt, grub_size_t saltlen, + const void *key, grub_size_t keylen, + const void *ad, grub_size_t adlen, + grub_size_t resultlen, void *result) +{ + gcry_kdf_hd_t hd = {0}; + gpg_err_code_t err; + + if (saltlen == 0) + return GPG_ERR_INV_VALUE; + + err = _gcry_kdf_open (&hd, GRUB_GCRY_KDF_ARGON2, subalgo, param, paramlen, + password, passwordlen, salt, saltlen, key, keylen, + ad, adlen); + if (err != GPG_ERR_NO_ERROR) + return err; + + err = _gcry_kdf_compute (hd, NULL); + if (err == GPG_ERR_NO_ERROR) + err = _gcry_kdf_final (hd, resultlen, result); + + _gcry_kdf_close (hd); + + return err; +} diff --git a/include/grub/crypto.h b/include/grub/crypto.h index 6758bae9a..125502582 100644 --- a/include/grub/crypto.h +++ b/include/grub/crypto.h @@ -582,6 +582,15 @@ grub_crypto_pbkdf2 (const struct gcry_md_spec *md, unsigned int c, grub_uint8_t *DK, grub_size_t dkLen); +gcry_err_code_t +grub_crypto_argon2 (int subalgo, + const unsigned long *param, unsigned int paramlen, + const void *password, grub_size_t passwordlen, + const void *salt, grub_size_t saltlen, + const void *key, grub_size_t keylen, + const void *ad, grub_size_t adlen, + grub_size_t resultlen, void *result); + int grub_crypto_memcmp (const void *a, const void *b, grub_size_t n); From 6052fc2cf684dffa507a9d81f9f8b4cbe170e6b6 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 15 Oct 2025 11:00:23 +0800 Subject: [PATCH 076/208] disk/luks2: Add Argon2 support Leverage the new grub_crypto_argon2() function to add support for the Argon2i and Argon2id KDFs in LUKS2. Signed-off-by: Gary Lin Tested-By: Waldemar Brodkorb Reviewed-by: Daniel Kiper --- grub-core/disk/luks2.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/grub-core/disk/luks2.c b/grub-core/disk/luks2.c index b17cd2115..a3608e233 100644 --- a/grub-core/disk/luks2.c +++ b/grub-core/disk/luks2.c @@ -39,6 +39,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); enum grub_luks2_kdf_type { LUKS2_KDF_TYPE_ARGON2I, + LUKS2_KDF_TYPE_ARGON2ID, LUKS2_KDF_TYPE_PBKDF2 }; typedef enum grub_luks2_kdf_type grub_luks2_kdf_type_t; @@ -159,13 +160,21 @@ luks2_parse_keyslot (grub_luks2_keyslot_t *out, const grub_json_t *keyslot) grub_json_getstring (&type, &kdf, "type") || grub_json_getstring (&out->kdf.salt, &kdf, "salt")) return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing or invalid KDF"); - else if (!grub_strcmp (type, "argon2i") || !grub_strcmp (type, "argon2id")) + else if (!grub_strcmp (type, "argon2i")) { out->kdf.type = LUKS2_KDF_TYPE_ARGON2I; if (grub_json_getint64 (&out->kdf.u.argon2i.time, &kdf, "time") || grub_json_getint64 (&out->kdf.u.argon2i.memory, &kdf, "memory") || grub_json_getint64 (&out->kdf.u.argon2i.cpus, &kdf, "cpus")) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing Argon2i parameters"); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing Argon2i parameters"); + } + else if (!grub_strcmp (type, "argon2id")) + { + out->kdf.type = LUKS2_KDF_TYPE_ARGON2ID; + if (grub_json_getint64 (&out->kdf.u.argon2i.time, &kdf, "time") || + grub_json_getint64 (&out->kdf.u.argon2i.memory, &kdf, "memory") || + grub_json_getint64 (&out->kdf.u.argon2i.cpus, &kdf, "cpus")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing Argon2id parameters"); } else if (!grub_strcmp (type, "pbkdf2")) { @@ -444,6 +453,8 @@ luks2_decrypt_key (grub_uint8_t *out_key, grub_uint8_t salt[GRUB_CRYPTODISK_MAX_KEYLEN]; grub_uint8_t *split_key = NULL; idx_t saltlen = sizeof (salt); + int subalgo; + unsigned long param[4]; char cipher[32], *p; const gcry_md_spec_t *hash; gcry_err_code_t gcry_ret; @@ -460,8 +471,29 @@ luks2_decrypt_key (grub_uint8_t *out_key, switch (k->kdf.type) { case LUKS2_KDF_TYPE_ARGON2I: - ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Argon2 not supported"); - goto err; + case LUKS2_KDF_TYPE_ARGON2ID: + if (k->kdf.type == LUKS2_KDF_TYPE_ARGON2I) + subalgo = GRUB_GCRY_KDF_ARGON2I; + else + subalgo = GRUB_GCRY_KDF_ARGON2ID; + + param[0] = k->area.key_size; + param[1] = k->kdf.u.argon2i.time; + param[2] = k->kdf.u.argon2i.memory; + param[3] = k->kdf.u.argon2i.cpus; + + gcry_ret = grub_crypto_argon2 (subalgo, param, 4, + passphrase, passphraselen, + salt, saltlen, + NULL, 0, NULL, 0, + k->area.key_size, area_key); + if (gcry_ret) + { + ret = grub_crypto_gcry_error (gcry_ret); + goto err; + } + + break; case LUKS2_KDF_TYPE_PBKDF2: hash = grub_crypto_lookup_md_by_name (k->kdf.u.pbkdf2.hash); if (!hash) From 6a525ee64f1934c1e0c7d96da2d1462bc712e8c1 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 15 Oct 2025 11:00:24 +0800 Subject: [PATCH 077/208] tests: Import Argon2 tests from libgcrypt Copy the Argon2 test function, check_argon2(), from t-kdf.c in libgcrypt to grub-core/tests/argon2_test.c. Signed-off-by: Gary Lin Tested-By: Waldemar Brodkorb Reviewed-by: Daniel Kiper --- grub-core/tests/argon2_test.c | 141 ++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 grub-core/tests/argon2_test.c diff --git a/grub-core/tests/argon2_test.c b/grub-core/tests/argon2_test.c new file mode 100644 index 000000000..d2f70166a --- /dev/null +++ b/grub-core/tests/argon2_test.c @@ -0,0 +1,141 @@ +static void +check_argon2 (void) +{ + gcry_error_t err; + static struct { + int subalgo; + unsigned long param[4]; + size_t passlen; + const char *pass; + size_t saltlen; + const char *salt; + size_t keylen; + const char *key; + size_t adlen; + const char *ad; + size_t dklen; + const char *dk; + } tv[] = { + { + GCRY_KDF_ARGON2D, + { 32, 3, 32, 4 }, + 32, + "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" + "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01", + 16, + "\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02", + 8, + "\x03\x03\x03\x03\x03\x03\x03\x03", + 12, + "\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04", + 32, + "\x51\x2b\x39\x1b\x6f\x11\x62\x97\x53\x71\xd3\x09\x19\x73\x42\x94" + "\xf8\x68\xe3\xbe\x39\x84\xf3\xc1\xa1\x3a\x4d\xb9\xfa\xbe\x4a\xcb" + }, + { + GCRY_KDF_ARGON2I, + { 32, 3, 32, 4 }, + 32, + "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" + "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01", + 16, + "\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02", + 8, + "\x03\x03\x03\x03\x03\x03\x03\x03", + 12, + "\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04", + 32, + "\xc8\x14\xd9\xd1\xdc\x7f\x37\xaa\x13\xf0\xd7\x7f\x24\x94\xbd\xa1" + "\xc8\xde\x6b\x01\x6d\xd3\x88\xd2\x99\x52\xa4\xc4\x67\x2b\x6c\xe8" + }, + { + GCRY_KDF_ARGON2ID, + { 32, 3, 32, 4 }, + 32, + "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" + "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01", + 16, + "\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02", + 8, + "\x03\x03\x03\x03\x03\x03\x03\x03", + 12, + "\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04", + 32, + "\x0d\x64\x0d\xf5\x8d\x78\x76\x6c\x08\xc0\x37\xa3\x4a\x8b\x53\xc9" + "\xd0\x1e\xf0\x45\x2d\x75\xb6\x5e\xb5\x25\x20\xe9\x6b\x01\xe6\x59" + }, + { + /* empty password */ + GCRY_KDF_ARGON2I, + { 32, 3, 128, 1 }, + 0, NULL, + 16, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + 0, NULL, + 0, NULL, + 32, + "\xbb\x1f\xf2\xb9\x9f\xd4\x4a\xd9\xdf\x7f\xb9\x54\x55\x9e\xb8\xeb" + "\xb5\x9d\xab\xce\x2e\x62\x9f\x9b\x89\x09\xfe\xde\x57\xcc\x63\x86" + }, + { + /* empty password */ + GCRY_KDF_ARGON2ID, + { 32, 3, 128, 1 }, + 0, NULL, + 16, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + 0, NULL, + 0, NULL, + 32, + "\x09\x2f\x38\x35\xac\xb2\x43\x92\x93\xeb\xcd\xe8\x04\x16\x6a\x31" + "\xce\x14\xd4\x55\xdb\xd8\xf7\xe6\xb4\xf5\x9d\x64\x8e\xd0\x3a\xdb" + }, + }; + unsigned char out[32]; + int i; + int count; + + for (count = 0; count < DIM(tv); count++) + { + if (verbose) + fprintf (stderr, "checking ARGON2 test vector %d\n", count); + + err = my_kdf_derive (0, GCRY_KDF_ARGON2, + tv[count].subalgo, tv[count].param, 4, + tv[count].pass, tv[count].passlen, + tv[count].salt, tv[count].saltlen, + tv[count].key, tv[count].keylen, + tv[count].ad, tv[count].adlen, + tv[count].dklen, out); + if (err) + fail ("argon2 test %d failed: %s\n", count*2+0, gpg_strerror (err)); + else if (memcmp (out, tv[count].dk, tv[count].dklen)) + { + fail ("argon2 test %d failed: mismatch\n", count*2+0); + fputs ("got:", stderr); + for (i=0; i < tv[count].dklen; i++) + fprintf (stderr, " %02x", out[i]); + putc ('\n', stderr); + } + +#ifdef HAVE_PTHREAD + err = my_kdf_derive (1, GCRY_KDF_ARGON2, + tv[count].subalgo, tv[count].param, 4, + tv[count].pass, tv[count].passlen, + tv[count].salt, tv[count].saltlen, + tv[count].key, tv[count].keylen, + tv[count].ad, tv[count].adlen, + tv[count].dklen, out); + if (err) + fail ("argon2 test %d failed: %s\n", count*2+1, gpg_strerror (err)); + else if (memcmp (out, tv[count].dk, tv[count].dklen)) + { + fail ("argon2 test %d failed: mismatch\n", count*2+1); + fputs ("got:", stderr); + for (i=0; i < tv[count].dklen; i++) + fprintf (stderr, " %02x", out[i]); + putc ('\n', stderr); + } +#endif + } +} From c1bd9fc829de800dc85c347ac3fc254a2ea5c791 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 15 Oct 2025 11:00:25 +0800 Subject: [PATCH 078/208] tests: Integrate Argon2 tests into functional_test Refactor the Argon2 tests to enable the module build and integrate the tests into function_test. Signed-off-by: Gary Lin Tested-By: Waldemar Brodkorb Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/Makefile.core.def | 5 ++ grub-core/tests/argon2_test.c | 104 +++++++++++++------------- grub-core/tests/lib/functional_test.c | 1 + 3 files changed, 57 insertions(+), 53 deletions(-) diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index b378b9b60..2b84d76d2 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2265,6 +2265,11 @@ module = { common = tests/pbkdf2_test.c; }; +module = { + name = argon2_test; + common = tests/argon2_test.c; +}; + module = { name = legacy_password_test; common = tests/legacy_password_test.c; diff --git a/grub-core/tests/argon2_test.c b/grub-core/tests/argon2_test.c index d2f70166a..8318a0962 100644 --- a/grub-core/tests/argon2_test.c +++ b/grub-core/tests/argon2_test.c @@ -1,23 +1,50 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2025 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define DIM(v) (sizeof(v)/sizeof((v)[0])) + static void -check_argon2 (void) +argon2_test (void) { gcry_error_t err; static struct { int subalgo; unsigned long param[4]; - size_t passlen; + grub_size_t passlen; const char *pass; - size_t saltlen; + grub_size_t saltlen; const char *salt; - size_t keylen; + grub_size_t keylen; const char *key; - size_t adlen; + grub_size_t adlen; const char *ad; - size_t dklen; + grub_size_t dklen; const char *dk; } tv[] = { { - GCRY_KDF_ARGON2D, + GRUB_GCRY_KDF_ARGON2D, { 32, 3, 32, 4 }, 32, "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" @@ -33,7 +60,7 @@ check_argon2 (void) "\xf8\x68\xe3\xbe\x39\x84\xf3\xc1\xa1\x3a\x4d\xb9\xfa\xbe\x4a\xcb" }, { - GCRY_KDF_ARGON2I, + GRUB_GCRY_KDF_ARGON2I, { 32, 3, 32, 4 }, 32, "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" @@ -49,7 +76,7 @@ check_argon2 (void) "\xc8\xde\x6b\x01\x6d\xd3\x88\xd2\x99\x52\xa4\xc4\x67\x2b\x6c\xe8" }, { - GCRY_KDF_ARGON2ID, + GRUB_GCRY_KDF_ARGON2ID, { 32, 3, 32, 4 }, 32, "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" @@ -66,7 +93,7 @@ check_argon2 (void) }, { /* empty password */ - GCRY_KDF_ARGON2I, + GRUB_GCRY_KDF_ARGON2I, { 32, 3, 128, 1 }, 0, NULL, 16, @@ -79,7 +106,7 @@ check_argon2 (void) }, { /* empty password */ - GCRY_KDF_ARGON2ID, + GRUB_GCRY_KDF_ARGON2ID, { 32, 3, 128, 1 }, 0, NULL, 16, @@ -92,50 +119,21 @@ check_argon2 (void) }, }; unsigned char out[32]; - int i; - int count; + unsigned int count; for (count = 0; count < DIM(tv); count++) { - if (verbose) - fprintf (stderr, "checking ARGON2 test vector %d\n", count); - - err = my_kdf_derive (0, GCRY_KDF_ARGON2, - tv[count].subalgo, tv[count].param, 4, - tv[count].pass, tv[count].passlen, - tv[count].salt, tv[count].saltlen, - tv[count].key, tv[count].keylen, - tv[count].ad, tv[count].adlen, - tv[count].dklen, out); - if (err) - fail ("argon2 test %d failed: %s\n", count*2+0, gpg_strerror (err)); - else if (memcmp (out, tv[count].dk, tv[count].dklen)) - { - fail ("argon2 test %d failed: mismatch\n", count*2+0); - fputs ("got:", stderr); - for (i=0; i < tv[count].dklen; i++) - fprintf (stderr, " %02x", out[i]); - putc ('\n', stderr); - } - -#ifdef HAVE_PTHREAD - err = my_kdf_derive (1, GCRY_KDF_ARGON2, - tv[count].subalgo, tv[count].param, 4, - tv[count].pass, tv[count].passlen, - tv[count].salt, tv[count].saltlen, - tv[count].key, tv[count].keylen, - tv[count].ad, tv[count].adlen, - tv[count].dklen, out); - if (err) - fail ("argon2 test %d failed: %s\n", count*2+1, gpg_strerror (err)); - else if (memcmp (out, tv[count].dk, tv[count].dklen)) - { - fail ("argon2 test %d failed: mismatch\n", count*2+1); - fputs ("got:", stderr); - for (i=0; i < tv[count].dklen; i++) - fprintf (stderr, " %02x", out[i]); - putc ('\n', stderr); - } -#endif + err = grub_crypto_argon2 (tv[count].subalgo, + tv[count].param, 4, + tv[count].pass, tv[count].passlen, + tv[count].salt, tv[count].saltlen, + tv[count].key, tv[count].keylen, + tv[count].ad, tv[count].adlen, + tv[count].dklen, out); + grub_test_assert (err == 0, "argon2 test %d failed: %d", count, err); + grub_test_assert (grub_memcmp (out, tv[count].dk, tv[count].dklen) == 0, + "argon2 test %d failed: mismatch", count); } } + +GRUB_FUNCTIONAL_TEST (argon2_test, argon2_test); diff --git a/grub-core/tests/lib/functional_test.c b/grub-core/tests/lib/functional_test.c index 23048919d..94a606bf9 100644 --- a/grub-core/tests/lib/functional_test.c +++ b/grub-core/tests/lib/functional_test.c @@ -81,6 +81,7 @@ grub_functional_all_tests (grub_extcmd_context_t ctxt __attribute__ ((unused)), grub_dl_load ("mul_test"); grub_dl_load ("shift_test"); grub_dl_load ("asn1_test"); + grub_dl_load ("argon2_test"); FOR_LIST_ELEMENTS (test, grub_test_list) ok = !grub_test_run (test) && ok; From da01eb0c55a06fd89516d159e8310d80fcc610b9 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 15 Oct 2025 11:00:26 +0800 Subject: [PATCH 079/208] tests/util/grub-fs-tester: Use Argon2id for LUKS2 test Given that the LUKS1 test already covers PBKDF2, the default KDF for the LUKS2 test has been switched to Argon2id to ensure both algorithms are validated. Signed-off-by: Gary Lin Tested-By: Waldemar Brodkorb Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- tests/util/grub-fs-tester.in | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in index cac58dafa..126f19a7e 100644 --- a/tests/util/grub-fs-tester.in +++ b/tests/util/grub-fs-tester.in @@ -860,8 +860,14 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do MOUNTDEVICE="/dev/mapper/grub_test-testvol" MOUNTFS=ext2 "mkfs.ext2" -L "$FSLABEL" -q "${MOUNTDEVICE}" ;; - x"luks"*) - echo -n "$PASS" | cryptsetup luksFormat --type "$fs" --sector-size $SECSIZE --pbkdf pbkdf2 --force-password --disable-locks $LODEVICE + xluks1) + echo -n "$PASS" | cryptsetup luksFormat --type luks1 --sector-size $SECSIZE --pbkdf pbkdf2 --force-password --disable-locks $LODEVICE + echo -n "$PASS" | cryptsetup open --disable-locks $LODEVICE "$DMNAME" + MOUNTDEVICE="/dev/mapper/${DMNAME}" + MOUNTFS=ext2 + "mkfs.ext2" -L "$FSLABEL" -q "${MOUNTDEVICE}" ;; + xluks2) + echo -n "$PASS" | cryptsetup luksFormat --type luks2 --sector-size $SECSIZE --pbkdf argon2id --force-password --disable-locks $LODEVICE echo -n "$PASS" | cryptsetup open --disable-locks $LODEVICE "$DMNAME" MOUNTDEVICE="/dev/mapper/${DMNAME}" MOUNTFS=ext2 From 28dbe8a3b6b3c29e5b61c55c5525d0faec8d93cc Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 15 Oct 2025 11:00:28 +0800 Subject: [PATCH 080/208] kern/misc: Implement faster grub_memcpy() for aligned buffers When both "dest" and "src" are aligned, copying the data in grub_addr_t sized chunks is more efficient than a byte-by-byte copy. Also tweak __aeabi_memcpy(), __aeabi_memcpy4(), and __aeabi_memcpy8(), since grub_memcpy() is not inline anymore. Optimization for unaligned buffers was omitted to maintain code simplicity and readability. The current chunk-copy optimization for aligned buffers already provides a noticeable performance improvement (*) for Argon2 keyslot decryption. (*) On my system, for a LUKS2 keyslot configured with a 1 GB Argon2 memory requirement, this patch reduces the decryption time from 22 seconds to 12 seconds. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- grub-core/boot/decompressor/minilib.c | 6 ++++++ grub-core/kern/compiler-rt.c | 8 +++---- grub-core/kern/misc.c | 31 +++++++++++++++++++++++++++ include/grub/misc.h | 8 +------ 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/grub-core/boot/decompressor/minilib.c b/grub-core/boot/decompressor/minilib.c index fc46ee07b..1fe3efaae 100644 --- a/grub-core/boot/decompressor/minilib.c +++ b/grub-core/boot/decompressor/minilib.c @@ -50,6 +50,12 @@ grub_memmove (void *dest, const void *src, grub_size_t n) return dest; } +void * +grub_memcpy (void *dest, const void *src, grub_size_t n) +{ + return grub_memmove (dest, src, n); +} + int grub_memcmp (const void *s1, const void *s2, grub_size_t n) { diff --git a/grub-core/kern/compiler-rt.c b/grub-core/kern/compiler-rt.c index eda689a0c..8f3865e95 100644 --- a/grub-core/kern/compiler-rt.c +++ b/grub-core/kern/compiler-rt.c @@ -24,7 +24,7 @@ void * GRUB_BUILTIN_ATTR memcpy (void *dest, const void *src, grub_size_t n) { - return grub_memmove (dest, src, n); + return grub_memcpy (dest, src, n); } void * GRUB_BUILTIN_ATTR memmove (void *dest, const void *src, grub_size_t n) @@ -372,11 +372,11 @@ grub_int32_t __aeabi_idiv (grub_int32_t a, grub_int32_t b) __attribute__ ((alias ("__divsi3"))); void *__aeabi_memcpy (void *dest, const void *src, grub_size_t n) - __attribute__ ((alias ("grub_memcpy"))); + __attribute__ ((alias ("memcpy"))); void *__aeabi_memcpy4 (void *dest, const void *src, grub_size_t n) - __attribute__ ((alias ("grub_memcpy"))); + __attribute__ ((alias ("memcpy"))); void *__aeabi_memcpy8 (void *dest, const void *src, grub_size_t n) - __attribute__ ((alias ("grub_memcpy"))); + __attribute__ ((alias ("memcpy"))); void *__aeabi_memset (void *s, int c, grub_size_t n) __attribute__ ((alias ("memset"))); diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index b9ac86c18..2faff00ea 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -99,6 +99,37 @@ grub_memmove (void *dest, const void *src, grub_size_t n) return dest; } +static void * +__memcpy_aligned (void *dest, const void *src, grub_size_t n) +{ + grub_addr_t *dw = (grub_addr_t *) dest; + const grub_addr_t *sw = (const grub_addr_t *) src; + grub_uint8_t *d; + const grub_uint8_t *s; + + for (; n >= sizeof (grub_addr_t); n -= sizeof (grub_addr_t)) + *dw++ = *sw++; + + d = (grub_uint8_t *) dw; + s = (const grub_uint8_t *) sw; + for (; n > 0; n--) + *d++ = *s++; + + return dest; +} + +void * +grub_memcpy (void *dest, const void *src, grub_size_t n) +{ + /* Check if dest and src are aligned and n >= sizeof(grub_addr_t). */ + if (((grub_addr_t) dest & (sizeof (grub_addr_t) - 1)) == 0 && + ((grub_addr_t) src & (sizeof (grub_addr_t) - 1)) == 0 && + n >= sizeof (grub_addr_t)) + return __memcpy_aligned (dest, src, n); + + return grub_memmove (dest, src, n); +} + char * grub_strcpy (char *dest, const char *src) { diff --git a/include/grub/misc.h b/include/grub/misc.h index 0d2bab7dd..1bb63231a 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -38,6 +38,7 @@ #define grub_dprintf(condition, ...) grub_real_dprintf(GRUB_FILE, __FUNCTION__, __LINE__, condition, __VA_ARGS__) void *EXPORT_FUNC(grub_memmove) (void *dest, const void *src, grub_size_t n); +void *EXPORT_FUNC(grub_memcpy) (void *dest, const void *src, grub_size_t n); char *EXPORT_FUNC(grub_strcpy) (char *dest, const char *src); static inline char * @@ -103,13 +104,6 @@ grub_strlcpy (char *dest, const char *src, grub_size_t size) return res; } -/* XXX: If grub_memmove is too slow, we must implement grub_memcpy. */ -static inline void * -grub_memcpy (void *dest, const void *src, grub_size_t n) -{ - return grub_memmove (dest, src, n); -} - #if defined(__x86_64__) && !defined (GRUB_UTIL) #if defined (__MINGW32__) || defined (__CYGWIN__) || defined (__MINGW64__) #define GRUB_ASM_ATTR __attribute__ ((sysv_abi)) From 59304a7b51ebee7a0e9001b56f7cbe9a37f88b82 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 15 Oct 2025 11:00:27 +0800 Subject: [PATCH 081/208] docs: Document argon2 and argon2_test modules Signed-off-by: Gary Lin Tested-By: Waldemar Brodkorb Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- docs/grub.texi | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/grub.texi b/docs/grub.texi index 25e602a56..52a98a97d 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -3976,6 +3976,8 @@ Modules can be loaded via the @command{insmod} (@pxref{insmod}) command. * aout_module:: * appleldr_module:: * archelp_module:: +* argon2_module:: +* argon2_test_module:: * at_keyboard_module:: * ata_module:: * backtrace_module:: @@ -4343,6 +4345,15 @@ computer (Intel based Macs). This module provides Archive Helper functions for archive based file systems such as TAR and CPIO archives. +@node argon2_module +@section argon2 +This module provides support for the Argon2 key derivation function. + +@node argon2_test_module +@section argon2_test +This module is intended for performing a functional test of the Argon2 +operation in GRUB. + @node at_keyboard_module @section at_keyboard This module provides support for the AT keyboard input for the GRUB terminal. From 961e38b2b03c50a50859dbc484559b9b848e8d84 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Thu, 14 Aug 2025 14:32:41 +0800 Subject: [PATCH 082/208] lib/crypto: Introduce new HMAC functions to reuse buffers To enable more efficient buffer reuse for HMAC operations three new functions have been introduced. This change prevents the need to reallocate memory for each HMAC operation: - grub_crypto_hmac_reset(): reinitializes the hash contexts in the HMAC handle, - grub_crypto_hmac_final(): provides the final HMAC result without freeing the handle allowing it to be reused immediately, - grub_crypto_hmac_free(): deallocates the HMAC handle and its associated memory. To further facilitate buffer reuse ctx2 is now included within the HMAC handle struct and the initialization of ctx2 is moved to grub_crypto_hmac_init(). The intermediate hash states, ctx and ctx2, for the inner and outer padded keys are now cached. The grub_crypto_hmac_reset() restores these cached states for new operations which avoids redundant hashing of the keys. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- grub-core/disk/geli.c | 4 +- grub-core/lib/crypto.c | 91 ++++++++++++++++++++++++++++++------------ include/grub/crypto.h | 8 +++- 3 files changed, 74 insertions(+), 29 deletions(-) diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c index 722910d64..bf22ebb63 100644 --- a/grub-core/disk/geli.c +++ b/grub-core/disk/geli.c @@ -464,9 +464,7 @@ geli_recover_key (grub_disk_t source, grub_cryptodisk_t dev, grub_cryptomount_ar grub_crypto_hmac_write (hnd, header.salt, sizeof (header.salt)); grub_crypto_hmac_write (hnd, cargs->key_data, cargs->key_len); - gcry_err = grub_crypto_hmac_fini (hnd, geomkey); - if (gcry_err) - return grub_crypto_gcry_error (gcry_err); + grub_crypto_hmac_fini (hnd, geomkey); } gcry_err = grub_crypto_hmac_buffer (dev->hash, geomkey, diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c index 292b747b2..c29eeb333 100644 --- a/grub-core/lib/crypto.c +++ b/grub-core/lib/crypto.c @@ -31,7 +31,9 @@ struct grub_crypto_hmac_handle { const struct gcry_md_spec *md; void *ctx; - void *opad; + void *ctx2; + void *ctx_cache; + void *ctx2_cache; }; static gcry_cipher_spec_t *grub_ciphers = NULL; @@ -443,7 +445,8 @@ grub_crypto_hmac_init (const struct gcry_md_spec *md, { grub_uint8_t *helpkey = NULL; grub_uint8_t *ipad = NULL, *opad = NULL; - void *ctx = NULL; + void *ctx = NULL, *ctx2 = NULL; + void *ctx_cache = NULL, *ctx2_cache = NULL; struct grub_crypto_hmac_handle *ret = NULL; unsigned i; @@ -454,6 +457,18 @@ grub_crypto_hmac_init (const struct gcry_md_spec *md, if (!ctx) goto err; + ctx2 = grub_malloc (md->contextsize); + if (!ctx2) + goto err; + + ctx_cache = grub_malloc (md->contextsize); + if (!ctx_cache) + goto err; + + ctx2_cache = grub_malloc (md->contextsize); + if (!ctx2_cache) + goto err; + if ( keylen > md->blocksize ) { helpkey = grub_malloc (md->mdlen); @@ -483,26 +498,40 @@ grub_crypto_hmac_init (const struct gcry_md_spec *md, grub_free (helpkey); helpkey = NULL; + /* inner pad */ md->init (ctx, 0); - - md->write (ctx, ipad, md->blocksize); /* inner pad */ + md->write (ctx, ipad, md->blocksize); + grub_memcpy (ctx_cache, ctx, md->contextsize); grub_memset (ipad, 0, md->blocksize); grub_free (ipad); ipad = NULL; + /* outer pad */ + md->init (ctx2, 0); + md->write (ctx2, opad, md->blocksize); + grub_memcpy (ctx2_cache, ctx2, md->contextsize); + grub_memset (opad, 0, md->blocksize); + grub_free (opad); + opad = NULL; + ret = grub_malloc (sizeof (*ret)); if (!ret) goto err; ret->md = md; ret->ctx = ctx; - ret->opad = opad; + ret->ctx2 = ctx2; + ret->ctx_cache = ctx_cache; + ret->ctx2_cache = ctx2_cache; return ret; err: grub_free (helpkey); grub_free (ctx); + grub_free (ctx2); + grub_free (ctx_cache); + grub_free (ctx2_cache); grub_free (ipad); grub_free (opad); return NULL; @@ -516,37 +545,48 @@ grub_crypto_hmac_write (struct grub_crypto_hmac_handle *hnd, hnd->md->write (hnd->ctx, data, datalen); } -gcry_err_code_t +void grub_crypto_hmac_fini (struct grub_crypto_hmac_handle *hnd, void *out) { - grub_uint8_t *p; - grub_uint8_t *ctx2; + grub_crypto_hmac_final (hnd, out); + grub_crypto_hmac_free (hnd); +} - ctx2 = grub_malloc (hnd->md->contextsize); - if (!ctx2) - return GPG_ERR_OUT_OF_MEMORY; +void +grub_crypto_hmac_reset (struct grub_crypto_hmac_handle *hnd) +{ + grub_memcpy (hnd->ctx, hnd->ctx_cache, hnd->md->contextsize); + grub_memcpy (hnd->ctx2, hnd->ctx2_cache, hnd->md->contextsize); +} + +void +grub_crypto_hmac_final (struct grub_crypto_hmac_handle *hnd, void *out) +{ + grub_uint8_t *p; hnd->md->final (hnd->ctx); hnd->md->read (hnd->ctx); p = hnd->md->read (hnd->ctx); - hnd->md->init (ctx2, 0); - hnd->md->write (ctx2, hnd->opad, hnd->md->blocksize); - hnd->md->write (ctx2, p, hnd->md->mdlen); - hnd->md->final (ctx2); - grub_memset (hnd->opad, 0, hnd->md->blocksize); - grub_free (hnd->opad); + hnd->md->write (hnd->ctx2, p, hnd->md->mdlen); + hnd->md->final (hnd->ctx2); + + grub_memcpy (out, hnd->md->read (hnd->ctx2), hnd->md->mdlen); +} + +void +grub_crypto_hmac_free (struct grub_crypto_hmac_handle *hnd) +{ grub_memset (hnd->ctx, 0, hnd->md->contextsize); grub_free (hnd->ctx); - - grub_memcpy (out, hnd->md->read (ctx2), hnd->md->mdlen); - grub_memset (ctx2, 0, hnd->md->contextsize); - grub_free (ctx2); - + grub_memset (hnd->ctx2, 0, hnd->md->contextsize); + grub_free (hnd->ctx2); + grub_memset (hnd->ctx_cache, 0, hnd->md->contextsize); + grub_free (hnd->ctx_cache); + grub_memset (hnd->ctx2_cache, 0, hnd->md->contextsize); + grub_free (hnd->ctx2_cache); grub_memset (hnd, 0, sizeof (*hnd)); grub_free (hnd); - - return GPG_ERR_NO_ERROR; } gcry_err_code_t @@ -561,7 +601,8 @@ grub_crypto_hmac_buffer (const struct gcry_md_spec *md, return GPG_ERR_OUT_OF_MEMORY; grub_crypto_hmac_write (hnd, data, datalen); - return grub_crypto_hmac_fini (hnd, out); + grub_crypto_hmac_fini (hnd, out); + return GPG_ERR_NO_ERROR; } diff --git a/include/grub/crypto.h b/include/grub/crypto.h index 125502582..68f5c10c3 100644 --- a/include/grub/crypto.h +++ b/include/grub/crypto.h @@ -511,8 +511,14 @@ void grub_crypto_hmac_write (struct grub_crypto_hmac_handle *hnd, const void *data, grub_size_t datalen); -gcry_err_code_t +void grub_crypto_hmac_fini (struct grub_crypto_hmac_handle *hnd, void *out); +void +grub_crypto_hmac_reset (struct grub_crypto_hmac_handle *hnd); +void +grub_crypto_hmac_final (struct grub_crypto_hmac_handle *hnd, void *out); +void +grub_crypto_hmac_free (struct grub_crypto_hmac_handle *hnd); gcry_err_code_t grub_crypto_hmac_buffer (const struct gcry_md_spec *md, From a122e02623caaca4cc22a4e885da92784a70a88b Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Thu, 14 Aug 2025 14:32:42 +0800 Subject: [PATCH 083/208] lib/pbkdf2: Optimize PBKDF2 by reusing HMAC handle The previous PBKDF2 implementation used grub_crypto_hmac_buffer() which allocates and frees an HMAC handle on every call. This approach caused significant performance overhead slowing down the boot process considerably. This commit refactors the PBKDF2 code to use the new HMAC functions allowing the HMAC handle and its buffers to be allocated once and reused across multiple operations. This change significantly reduces disk unlocking time. In a QEMU/OVMF test environment this patch reduced the time to unlock a LUKS2 (*) partition from approximately 15 seconds to 4 seconds. (*) PBKDF2 SHA256 with 3454944 iterations. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- grub-core/lib/pbkdf2.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/grub-core/lib/pbkdf2.c b/grub-core/lib/pbkdf2.c index 28aa96c46..410eff580 100644 --- a/grub-core/lib/pbkdf2.c +++ b/grub-core/lib/pbkdf2.c @@ -39,6 +39,7 @@ grub_crypto_pbkdf2 (const struct gcry_md_spec *md, unsigned int c, grub_uint8_t *DK, grub_size_t dkLen) { + struct grub_crypto_hmac_handle *hnd = NULL; unsigned int hLen = md->mdlen; grub_uint8_t U[GRUB_CRYPTO_MAX_MDLEN]; grub_uint8_t T[GRUB_CRYPTO_MAX_MDLEN]; @@ -47,7 +48,6 @@ grub_crypto_pbkdf2 (const struct gcry_md_spec *md, unsigned int r; unsigned int i; unsigned int k; - gcry_err_code_t rc; grub_uint8_t *tmp; grub_size_t tmplen = Slen + 4; @@ -72,6 +72,13 @@ grub_crypto_pbkdf2 (const struct gcry_md_spec *md, grub_memcpy (tmp, S, Slen); + hnd = grub_crypto_hmac_init (md, P, Plen); + if (hnd == NULL) + { + grub_free (tmp); + return GPG_ERR_OUT_OF_MEMORY; + } + for (i = 1; i - 1 < l; i++) { grub_memset (T, 0, hLen); @@ -85,16 +92,13 @@ grub_crypto_pbkdf2 (const struct gcry_md_spec *md, tmp[Slen + 2] = (i & 0x0000ff00) >> 8; tmp[Slen + 3] = (i & 0x000000ff) >> 0; - rc = grub_crypto_hmac_buffer (md, P, Plen, tmp, tmplen, U); + grub_crypto_hmac_write (hnd, tmp, tmplen); } else - rc = grub_crypto_hmac_buffer (md, P, Plen, U, hLen, U); + grub_crypto_hmac_write (hnd, U, hLen); - if (rc != GPG_ERR_NO_ERROR) - { - grub_free (tmp); - return rc; - } + grub_crypto_hmac_final (hnd, U); + grub_crypto_hmac_reset (hnd); for (k = 0; k < hLen; k++) T[k] ^= U[k]; @@ -103,6 +107,7 @@ grub_crypto_pbkdf2 (const struct gcry_md_spec *md, grub_memcpy (DK + (i - 1) * hLen, T, i == l ? r : hLen); } + grub_crypto_hmac_free (hnd); grub_free (tmp); return GPG_ERR_NO_ERROR; From d01abd71311a0e9f9f0e59bc0d157806d5554541 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 22 Oct 2025 09:28:51 +0800 Subject: [PATCH 084/208] configure: Tweak autoconf/automake files to detect x86_64 features To enable hardware acceleration, this commit ports the feature detection logic from libgcrypt. This allows us to check if the compiler supports specific assembly instructions, including SSSE3, Intel SHA extensions, SSE4.1, AVX, AVX2, AVX512, and BMI2. To simplify the initial implementation, support for x86_64 feature detection is currently limited to the x86_64 EFI target. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- conf/Makefile.common | 2 + configure.ac | 233 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+) diff --git a/conf/Makefile.common b/conf/Makefile.common index 4d38ff034..7ef171b2b 100644 --- a/conf/Makefile.common +++ b/conf/Makefile.common @@ -24,6 +24,8 @@ if COND_HAVE_PCI CFLAGS_PLATFORM += -DGRUB_HAS_PCI endif +CPPFLAGS_GCRY_ASM = @CPPFLAGS_GCRY_ASM@ + # Other options CPPFLAGS_DEFAULT = -DGRUB_FILE=\"$(subst $(srcdir)/,,$<)\" diff --git a/configure.ac b/configure.ac index 1036638a9..1f391fa8e 100644 --- a/configure.ac +++ b/configure.ac @@ -1148,6 +1148,239 @@ if test "x$grub_cv_cc_fno_ident" = xyes; then TARGET_CFLAGS="$TARGET_CFLAGS -fno-ident" fi +# Implementation of the --disable-amd64-as-feature-detection switch. +AC_MSG_CHECKING([whether to enable AMD64 as(1) feature detection]) +if test x$target_cpu == xx86_64 -a x$platform == xefi; then + CPPFLAGS_GCRY_ASM="-D__x86_64 -DHAVE_CPU_ARCH_X86" + AC_ARG_ENABLE(amd64-as-feature-detection, + AS_HELP_STRING([--disable-amd64-as-feature-detection], + [Disable the auto-detection of AMD64 as(1) features]), + [amd64_as_feature_detection=$enableval], + [amd64_as_feature_detection=yes]) +else + CPPFLAGS_GCRY_ASM= + amd64_as_feature_detection=no +fi +AC_MSG_RESULT($amd64_as_feature_detection) + +# +# Check whether GCC assembler supports features needed for libgcrypt amd64 +# implementations +# +if test $amd64_as_feature_detection = yes; then + AC_CACHE_CHECK([whether GCC assembler is compatible for amd64 assembly implementations], + [grub_cv_gcc_x86_platform_as_ok], + [if test "$target_cpu" != "x86_64" ; then + grub_cv_gcc_x86_platform_as_ok="n/a" + else + grub_cv_gcc_x86_platform_as_ok=no + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[__asm__( + /* Test if '.type' and '.size' are supported. */ + /* These work only on ELF targets. */ + ".text\n\t" + "asmfunc:\n\t" + ".size asmfunc,.-asmfunc;\n\t" + ".type asmfunc,@function;\n\t" + /* Test if assembler allows use of '/' for constant division + * (Solaris/x86 issue). If previous constant division check + * and "-Wa,--divide" workaround failed, this causes assembly + * to be disable on this machine. */ + "xorl \$(123456789/12345678), %ebp;\n\t" + ); + void asmfunc(void);]], [ asmfunc(); ])], + [grub_cv_gcc_x86_platform_as_ok=yes]) + fi]) + if test "$grub_cv_gcc_x86_platform_as_ok" = "yes"; then + # Define __PIC__ to ensure the assembly code use PIC instructions + CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -D__PIC__=1 -DHAVE_COMPATIBLE_GCC_AMD64_PLATFORM_AS" + fi + + # + # Check whether GCC assembler supports Intel syntax + # + AC_CACHE_CHECK([whether GCC assembler is compatible for Intel syntax assembly implementations], + [grub_cv_gcc_platform_as_ok_for_intel_syntax], + [if test "$target_cpu" != "x86_64" ; then + grub_cv_gcc_platform_as_ok_for_intel_syntax="n/a" + else + grub_cv_gcc_platform_as_ok_for_intel_syntax=no + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[__asm__( + ".intel_syntax noprefix\n\t" + ".text\n\t" + "actest:\n\t" + "pxor xmm1, xmm7;\n\t" + "vperm2i128 ymm2, ymm3, ymm0, 1;\n\t" + "add eax, ebp;\n\t" + "rorx eax, ebp, 1;\n\t" + "sub eax, [esp + 4];\n\t" + "add dword ptr [esp + eax], 0b10101;\n\t" + ".att_syntax prefix\n\t" + ); + void actest(void);]], [ actest(); ])], + [grub_cv_gcc_platform_as_ok_for_intel_syntax=yes]) + fi]) + if test "$grub_cv_gcc_platform_as_ok_for_intel_syntax" = "yes" ; then + CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -DHAVE_INTEL_SYNTAX_PLATFORM_AS" + fi + + # + # Check whether GCC inline assembler supports SSSE3 instructions + # + AC_CACHE_CHECK([whether GCC inline assembler supports SSSE3 instructions], + [grub_cv_gcc_inline_asm_ssse3], + [if test "$target_cpu" != "x86_64" ; then + grub_cv_gcc_inline_asm_ssse3="n/a" + else + grub_cv_gcc_inline_asm_ssse3=no + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[static unsigned char be_mask[16] __attribute__ ((aligned (16))) = + { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; + void a(void) { + __asm__("pshufb %[mask], %%xmm2\n\t"::[mask]"m"(*be_mask):); + }]], [ a(); ] )], + [grub_cv_gcc_inline_asm_ssse3=yes]) + fi]) + if test "$grub_cv_gcc_inline_asm_ssse3" = "yes" ; then + CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -DHAVE_GCC_INLINE_ASM_SSSE3" + fi + + # + # Check whether GCC inline assembler supports SHA Extensions instructions. + # + AC_CACHE_CHECK([whether GCC inline assembler supports SHA Extensions instructions], + [grub_cv_gcc_inline_asm_shaext], + [if test "$target_cpu" != "x86_64" ; then + grub_cv_gcc_inline_asm_shaext="n/a" + else + grub_cv_gcc_inline_asm_shaext=no + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[void a(void) { + __asm__("sha1rnds4 \$0, %%xmm1, %%xmm3\n\t":::"cc"); + __asm__("sha1nexte %%xmm1, %%xmm3\n\t":::"cc"); + __asm__("sha1msg1 %%xmm1, %%xmm3\n\t":::"cc"); + __asm__("sha1msg2 %%xmm1, %%xmm3\n\t":::"cc"); + __asm__("sha256rnds2 %%xmm0, %%xmm1, %%xmm3\n\t":::"cc"); + __asm__("sha256msg1 %%xmm1, %%xmm3\n\t":::"cc"); + __asm__("sha256msg2 %%xmm1, %%xmm3\n\t":::"cc"); + }]], [ a(); ] )], + [grub_cv_gcc_inline_asm_shaext=yes]) + fi]) + if test "$grub_cv_gcc_inline_asm_shaext" = "yes" ; then + CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -DHAVE_GCC_INLINE_ASM_SHAEXT -DENABLE_SHAEXT_SUPPORT" + fi + + # + # Check whether GCC inline assembler supports SSE4.1 instructions. + # + AC_CACHE_CHECK([whether GCC inline assembler supports SSE4.1 instructions], + [grub_cv_gcc_inline_asm_sse41], + [if test "$target_cpu" != "x86_64" ; then + grub_cv_gcc_inline_asm_sse41="n/a" + else + grub_cv_gcc_inline_asm_sse41=no + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[void a(void) { + int i; + __asm__("pextrd \$2, %%xmm0, %[out]\n\t" : [out] "=m" (i)); + }]], [ a(); ] )], + [grub_cv_gcc_inline_asm_sse41=yes]) + fi]) + if test "$grub_cv_gcc_inline_asm_sse41" = "yes" ; then + CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -DHAVE_GCC_INLINE_ASM_SSE41" + fi + + # + # Check whether GCC inline assembler supports AVX instructions + # + AC_CACHE_CHECK([whether GCC inline assembler supports AVX instructions], + [grub_cv_gcc_inline_asm_avx], + [if test "$target_cpu" != "x86_64" ; then + grub_cv_gcc_inline_asm_avx="n/a" + else + grub_cv_gcc_inline_asm_avx=no + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[void a(void) { + __asm__("xgetbv; vaesdeclast (%[mem]),%%xmm0,%%xmm7\n\t"::[mem]"r"(0):); + }]], [ a(); ] )], + [grub_cv_gcc_inline_asm_avx=yes]) + fi]) + if test "$grub_cv_gcc_inline_asm_avx" = "yes" ; then + CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -DHAVE_GCC_INLINE_ASM_AVX" + fi + + # + # Check whether GCC inline assembler supports AVX2 instructions + # + AC_CACHE_CHECK([whether GCC inline assembler supports AVX2 instructions], + [grub_cv_gcc_inline_asm_avx2], + [if test "$target_cpu" != "x86_64" ; then + grub_cv_gcc_inline_asm_avx2="n/a" + else + grub_cv_gcc_inline_asm_avx2=no + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[void a(void) { + __asm__("xgetbv; vpbroadcastb %%xmm7,%%ymm1\n\t":::"cc"); + }]], [ a(); ] )], + [grub_cv_gcc_inline_asm_avx2=yes]) + fi]) + if test "$grub_cv_gcc_inline_asm_avx2" = "yes" ; then + CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -DHAVE_GCC_INLINE_ASM_AVX2" + fi + + # + # Check whether GCC inline assembler supports AVX512 instructions + # + AC_CACHE_CHECK([whether GCC inline assembler supports AVX512 instructions], + [grub_cv_gcc_inline_asm_avx512], + [if test "$target_cpu" != "x86_64" ; then + grub_cv_gcc_inline_asm_avx512="n/a" + else + grub_cv_gcc_inline_asm_avx512=no + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[void a(void) { + __asm__("xgetbv; vpopcntq %%zmm7, %%zmm1%{%%k1%}%{z%};\n\t":::"cc"); + __asm__("vpexpandb %%zmm3, %%zmm1;\n\t":::"cc"); + __asm__("vpxorq %%xmm7, %%xmm7, %%xmm7;\n\t":::"cc"); + __asm__("vpxorq %%ymm7, %%ymm7, %%ymm7;\n\t":::"cc"); + __asm__("vpxorq (%%eax)%{1to8%}, %%zmm7, %%zmm7;\n\t":::"cc"); + }]], [ a(); ] )], + [grub_cv_gcc_inline_asm_avx512=yes]) + fi]) + if test "$grub_cv_gcc_inline_asm_avx512" = "yes" ; then + CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -DHAVE_GCC_INLINE_ASM_AVX512" + fi + + # + # Check whether GCC inline assembler supports BMI2 instructions + # + AC_CACHE_CHECK([whether GCC inline assembler supports BMI2 instructions], + [grub_cv_gcc_inline_asm_bmi2], + [if test "$target_cpu" != "x86_64" ; then + grub_cv_gcc_inline_asm_bmi2="n/a" + else + grub_cv_gcc_inline_asm_bmi2=no + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[unsigned int a(unsigned int x, unsigned int y) { + unsigned int tmp1, tmp2; + asm ("rorxl %2, %1, %0" + : "=r" (tmp1) + : "rm0" (x), "J" (32 - ((23) & 31))); + asm ("andnl %2, %1, %0" + : "=r" (tmp2) + : "r0" (x), "rm" (y)); + return tmp1 + tmp2; + }]], [ a(1, 2); ] )], + [grub_cv_gcc_inline_asm_bmi2=yes]) + fi]) + if test "$grub_cv_gcc_inline_asm_bmi2" = "yes" ; then + CPPFLAGS_GCRY_ASM="$CPPFLAGS_GCRY_ASM -DHAVE_GCC_INLINE_ASM_BMI2" + fi +fi + +AC_SUBST(CPPFLAGS_GCRY_ASM) + CFLAGS="$TARGET_CFLAGS" From 06a5b88bafa0fbd6313125ffe956ad2b0f7dde76 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 22 Oct 2025 09:28:52 +0800 Subject: [PATCH 085/208] lib/hwfeatures-gcry: Introduce functions to manage hardware features This commit introduces the generic functions to manage the hardware features in libgcrypt. These functions are stubs for future platform-specific implementations: - grub_gcry_hwf_enabled() returns __gcry_use_hwf which indicates if the hardware features are enabled specifically by grub_enable_gcry_hwf(), - grub_enable_gcry_hwf() invokes the architecture specific enablement functions and sets __gcry_use_hwf to true, - grub_reset_gcry_hwf() invokes the architecture specific reset functions and sets __gcry_use_hwf to false. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- Makefile.util.def | 1 + grub-core/Makefile.core.def | 1 + grub-core/lib/hwfeatures-gcry.c | 43 +++++++++++++++++++++++++++++++++ include/grub/hwfeatures-gcry.h | 26 ++++++++++++++++++++ 4 files changed, 71 insertions(+) create mode 100644 grub-core/lib/hwfeatures-gcry.c create mode 100644 include/grub/hwfeatures-gcry.h diff --git a/Makefile.util.def b/Makefile.util.def index f8d4ae7d3..0196911e6 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -36,6 +36,7 @@ library = { common = grub-core/kern/misc.c; common = grub-core/kern/partition.c; common = grub-core/lib/crypto.c; + common = grub-core/lib/hwfeatures-gcry.c; common = grub-core/lib/json/json.c; common = grub-core/disk/luks.c; common = grub-core/disk/luks2.c; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 2b84d76d2..3173e66be 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1720,6 +1720,7 @@ module = { module = { name = crypto; common = lib/crypto.c; + common = lib/hwfeatures-gcry.c; extra_dist = lib/libgcrypt-grub/cipher/crypto.lst; }; diff --git a/grub-core/lib/hwfeatures-gcry.c b/grub-core/lib/hwfeatures-gcry.c new file mode 100644 index 000000000..652e67c43 --- /dev/null +++ b/grub-core/lib/hwfeatures-gcry.c @@ -0,0 +1,43 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2025 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include + +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static bool __gcry_use_hwf = false; + +bool +grub_gcry_hwf_enabled (void) +{ + return __gcry_use_hwf; +} + +void +grub_enable_gcry_hwf (void) +{ + __gcry_use_hwf = true; +} + +void +grub_reset_gcry_hwf (void) +{ + __gcry_use_hwf = false; +} diff --git a/include/grub/hwfeatures-gcry.h b/include/grub/hwfeatures-gcry.h new file mode 100644 index 000000000..2884f054a --- /dev/null +++ b/include/grub/hwfeatures-gcry.h @@ -0,0 +1,26 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2025 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef HWF_GCRY_HEADER +#define HWF_GCRY_HEADER 1 + +extern bool grub_gcry_hwf_enabled (void); +extern void grub_enable_gcry_hwf (void); +extern void grub_reset_gcry_hwf (void); + +#endif /* HWF_GCRY_HEADER */ From 8423176f1f29d398a0d5ae1c2961acef9c4751a2 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 22 Oct 2025 09:28:53 +0800 Subject: [PATCH 086/208] lib/hwfeatures-gcry: Enable SSE and AVX for x86_64 EFI Implement the necessary functions to dynamically enable SSE and AVX on x86_64 EFI systems when the hardware is capable. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- grub-core/Makefile.core.def | 1 + grub-core/lib/hwfeatures-gcry.c | 9 + grub-core/lib/x86_64/efi/hwfeatures-gcry.c | 246 +++++++++++++++++++++ include/grub/x86_64/cpuid.h | 1 + include/grub/x86_64/efi/hwfeatures-gcry.h | 25 +++ 5 files changed, 282 insertions(+) create mode 100644 grub-core/lib/x86_64/efi/hwfeatures-gcry.c create mode 100644 include/grub/x86_64/cpuid.h create mode 100644 include/grub/x86_64/efi/hwfeatures-gcry.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 3173e66be..3d4b1a3b4 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1721,6 +1721,7 @@ module = { name = crypto; common = lib/crypto.c; common = lib/hwfeatures-gcry.c; + x86_64_efi = lib/x86_64/efi/hwfeatures-gcry.c; extra_dist = lib/libgcrypt-grub/cipher/crypto.lst; }; diff --git a/grub-core/lib/hwfeatures-gcry.c b/grub-core/lib/hwfeatures-gcry.c index 652e67c43..acfa7be33 100644 --- a/grub-core/lib/hwfeatures-gcry.c +++ b/grub-core/lib/hwfeatures-gcry.c @@ -19,6 +19,9 @@ #include #include +#if defined (__x86_64__) && defined (GRUB_MACHINE_EFI) +#include +#endif GRUB_MOD_LICENSE ("GPLv3+"); @@ -33,11 +36,17 @@ grub_gcry_hwf_enabled (void) void grub_enable_gcry_hwf (void) { +#if defined (__x86_64__) && defined (GRUB_MACHINE_EFI) + grub_enable_gcry_hwf_x86_64_efi (); +#endif __gcry_use_hwf = true; } void grub_reset_gcry_hwf (void) { +#if defined (__x86_64__) && defined (GRUB_MACHINE_EFI) + grub_reset_gcry_hwf_x86_64_efi (); +#endif __gcry_use_hwf = false; } diff --git a/grub-core/lib/x86_64/efi/hwfeatures-gcry.c b/grub-core/lib/x86_64/efi/hwfeatures-gcry.c new file mode 100644 index 000000000..8e26c010d --- /dev/null +++ b/grub-core/lib/x86_64/efi/hwfeatures-gcry.c @@ -0,0 +1,246 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2025 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + +/* + * Older versions of GCC may reorder the inline asm, which can lead to + * unexpected behavior when reading the Control Registers. The __FORCE_ORDER + * macro is used to prevent this. + * + * Ref: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=aa5cacdc29d76a005cbbee018a47faa6e724dd2d + */ +#define __FORCE_ORDER "m"(*(unsigned int *) 0x1000UL) + +#define HW_FEATURE_X86_64_SSE (1 << 0) +#define HW_FEATURE_X86_64_AVX (1 << 1) + +static grub_uint32_t hw_features = 0; +static grub_uint64_t old_cr0, old_cr4, old_xcr0; + +static grub_uint64_t +read_cr0 (void) +{ + grub_uint64_t val; + + asm volatile ("mov %%cr0, %0" : "=r" (val) : __FORCE_ORDER); + return val; +} + +static grub_uint64_t +read_cr4 (void) +{ + grub_uint64_t val; + + asm volatile ("mov %%cr4,%0" : "=r" (val) : __FORCE_ORDER); + return val; +} + +static void +write_cr0 (grub_uint64_t val) +{ + asm volatile ("mov %0,%%cr4": "+r" (val) : : "memory"); +} + +static void +write_cr4 (grub_uint64_t val) +{ + asm volatile ("mov %0,%%cr4": "+r" (val) : : "memory"); +} + +static grub_uint32_t +get_cpuid_ecx (void) +{ + grub_uint32_t eax, ebx, ecx, edx; + + grub_cpuid (1, eax, ebx, ecx, edx); + + return ecx; +} + +static grub_uint32_t +get_cpuid_edx (void) +{ + grub_uint32_t eax, ebx, ecx, edx; + + grub_cpuid (1, eax, ebx, ecx, edx); + + return edx; +} + +static bool +enable_sse (void) +{ + grub_uint64_t cr0, cr4; + grub_uint32_t edx; + + edx = get_cpuid_edx (); + + /* Check CPUID.01H:EDX.FXSR[bit 24] and CPUID.01H:EDX.SSE[bit 25] */ + if ((edx & (3 << 24)) != (3 << 24)) + return false; + + cr0 = old_cr0 = read_cr0 (); + cr4 = old_cr4 = read_cr4 (); + + /* clear CR0.EM[bit 2] */ + if ((cr0 & (1 << 2)) != 0) + cr0 &= ~(1 << 2); + + /* Set CR0.MP[bit 1] */ + if ((cr0 & (1 << 1)) == 0) + cr0 |= (1 << 1); + + grub_dprintf ("hwfeatures", "CR0: 0x%"PRIxGRUB_UINT64_T" 0x%"PRIxGRUB_UINT64_T"\n", old_cr0, cr0); + if (old_cr0 != cr0) + write_cr0 (cr0); + + /* Set CR4.OSFXSR[bit 9] and CR4.OSXMMEXCPT[bit 10] */ + if ((cr4 & (3 << 9)) != (3 << 9)) + cr4 |= (3 << 9); + + grub_dprintf ("hwfeatures", "CR4: 0x%"PRIxGRUB_UINT64_T" 0x%"PRIxGRUB_UINT64_T"\n", old_cr4, cr4); + if (old_cr4 != cr4) + write_cr4 (cr4); + + return true; +} + +static grub_uint64_t +xgetbv (grub_uint32_t index) +{ + grub_uint32_t eax, edx; + + asm volatile ("xgetbv" : "=a" (eax), "=d" (edx) : "c" (index)); + + return eax + ((grub_uint64_t)edx << 32); +} + +static void +xsetbv (grub_uint32_t index, grub_uint64_t value) +{ + grub_uint32_t eax = (grub_uint32_t)value; + grub_uint32_t edx = (grub_uint32_t)(value >> 32); + + asm volatile ("xsetbv" :: "a" (eax), "d" (edx), "c" (index)); +} + +static bool +enable_avx (void) +{ + grub_uint64_t cr4; + grub_uint32_t ecx; + grub_uint64_t sse_avx_mask = (1 << 2) | (1 << 1); + grub_uint64_t xcr0; + + ecx = get_cpuid_ecx (); + + /* Check the following two bits + * - CPUID.01H:ECX.XSAVE[bit 26] + * If XSAVE is not supported, setting CR4.OSXSAVE will cause + * general-protection fault (#GP). + * - CPUID.01H:ECX.AVX[bit 28] + */ + grub_dprintf ("hwfeatures", "Check CPUID.01H:ECX 0x%"PRIuGRUB_UINT32_T"\n", ecx); + if ((ecx & (5 << 26)) != (5 << 26)) + return false; + + cr4 = read_cr4 (); + + /* Set CR4.OSXSAVE[bit 18] */ + if ((cr4 & (1 << 18)) == 0) + { + grub_dprintf ("hwfeatures", "Set CR4.OSXSAVE\n"); + cr4 |= 1 << 18; + write_cr4 (cr4); + } + + ecx = get_cpuid_ecx (); + + /* Check CPUID.01H:ECX.OSXSAVE[bit 27] */ + if ((ecx & (1 << 27)) == 0) + return false; + + xcr0 = old_xcr0 = xgetbv (0); + + /* Set XCR0[bit 1] and XCR0[bit 2] to enable SSE/AVX */ + if ((xcr0 & sse_avx_mask) != sse_avx_mask) + { + grub_dprintf ("hwfeatures", "Set XCR0[2:1] to 11b\n"); + xcr0 |= sse_avx_mask; + xsetbv (0, xcr0); + } + + return true; +} + +void +grub_enable_gcry_hwf_x86_64_efi (void) +{ + if (enable_sse () == true) + hw_features |= HW_FEATURE_X86_64_SSE; + + if (enable_avx () == true) + hw_features |= HW_FEATURE_X86_64_AVX; +} + +void +grub_reset_gcry_hwf_x86_64_efi (void) +{ + grub_uint64_t cr0, cr4, xcr0; + + if ((hw_features & HW_FEATURE_X86_64_AVX) != 0) + { + xcr0 = xgetbv (0); + if (xcr0 != old_xcr0) + { + /* + * Reset the AVX state with 'vzeroupper' before clearing XCR0[bit 2]. + * + * Ref: Intel 64 and IA-32 Architectures Software Developer's Manual + * - 13.3 ENABLING THE XSAVE FEATURE SET AND XSAVE-ENABLED FEATURES + * + * "As noted in Section 13.1, the processor will preserve AVX state + * unmodified if software clears XCR0[2]. However, clearing XCR0[2] + * while AVX state is not in its initial configuration may cause SSE + * instructions to incur a power and performance penalty." + */ + asm volatile ("vzeroupper" ::: "memory"); + xsetbv (0, old_xcr0); + } + } + + if ((hw_features & HW_FEATURE_X86_64_AVX) != 0 || (hw_features & HW_FEATURE_X86_64_SSE) != 0) + { + cr4 = read_cr4 (); + if (cr4 != old_cr4) + write_cr4 (old_cr4); + } + + if ((hw_features & HW_FEATURE_X86_64_SSE) != 0) + { + cr0 = read_cr0 (); + if (cr0 != old_cr0) + write_cr0 (old_cr0); + } + + hw_features = 0; +} diff --git a/include/grub/x86_64/cpuid.h b/include/grub/x86_64/cpuid.h new file mode 100644 index 000000000..acd93e3a7 --- /dev/null +++ b/include/grub/x86_64/cpuid.h @@ -0,0 +1 @@ +#include diff --git a/include/grub/x86_64/efi/hwfeatures-gcry.h b/include/grub/x86_64/efi/hwfeatures-gcry.h new file mode 100644 index 000000000..a8e671058 --- /dev/null +++ b/include/grub/x86_64/efi/hwfeatures-gcry.h @@ -0,0 +1,25 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2025 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef HWF_GCRY_X86_64_EFI_HEADER +#define HWF_GCRY_X86_64_EFI_HEADER 1 + +extern void grub_enable_gcry_hwf_x86_64_efi (void); +extern void grub_reset_gcry_hwf_x86_64_efi (void); + +#endif /* HWF_GCRY_X86_64_EFI_HEADER */ From 7f9c590aff94e26c08452e4d4486b8ce84a4769a Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 22 Oct 2025 09:28:54 +0800 Subject: [PATCH 087/208] libgcrypt: Copy sha256 x86_64 assembly files Copy the selected x86_64 assembly files to support hardware acceleration for sha256. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- autogen.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/autogen.sh b/autogen.sh index fbdb33879..0e3c23f65 100755 --- a/autogen.sh +++ b/autogen.sh @@ -52,6 +52,13 @@ for x in mpi-asm-defs.h mpih-add1.c mpih-sub1.c mpih-mul1.c mpih-mul2.c mpih-mul cp grub-core/lib/libgcrypt-grub/mpi/generic/"$x" grub-core/lib/libgcrypt-grub/mpi/"$x" done +for x in sha256-ssse3-amd64.S sha256-avx-amd64.S sha256-avx2-bmi2-amd64.S sha256-intel-shaext.c; do + if [ -h grub-core/lib/libgcrypt-grub/cipher/"$x" ] || [ -f grub-core/lib/libgcrypt-grub/cipher/"$x" ]; then + rm grub-core/lib/libgcrypt-grub/cipher/"$x" + fi + cp grub-core/lib/libgcrypt/cipher/"$x" grub-core/lib/libgcrypt-grub/cipher/"$x" +done + for x in grub-core/lib/libgcrypt-patches/*.patch; do patch -i $x -p1 done From 81235619114593e6c04549c92928222c00441d7e Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 22 Oct 2025 09:28:55 +0800 Subject: [PATCH 088/208] libgcrypt: Copy sha512 x86_64 assembly files Copy the selected x86_64 assembly files to support hardware acceleration for sha512. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- autogen.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autogen.sh b/autogen.sh index 0e3c23f65..7ff90cb93 100755 --- a/autogen.sh +++ b/autogen.sh @@ -52,7 +52,7 @@ for x in mpi-asm-defs.h mpih-add1.c mpih-sub1.c mpih-mul1.c mpih-mul2.c mpih-mul cp grub-core/lib/libgcrypt-grub/mpi/generic/"$x" grub-core/lib/libgcrypt-grub/mpi/"$x" done -for x in sha256-ssse3-amd64.S sha256-avx-amd64.S sha256-avx2-bmi2-amd64.S sha256-intel-shaext.c; do +for x in sha256-ssse3-amd64.S sha256-avx-amd64.S sha256-avx2-bmi2-amd64.S sha256-intel-shaext.c sha512-ssse3-amd64.S sha512-avx-amd64.S sha512-avx2-bmi2-amd64.S sha512-avx512-amd64.S; do if [ -h grub-core/lib/libgcrypt-grub/cipher/"$x" ] || [ -f grub-core/lib/libgcrypt-grub/cipher/"$x" ]; then rm grub-core/lib/libgcrypt-grub/cipher/"$x" fi From 0ff5faf8cd1a2bd9bda60620a1701b5da48ad803 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 22 Oct 2025 09:28:56 +0800 Subject: [PATCH 089/208] libgcrypt: Implement _gcry_get_hw_features() Implement _gcry_get_hw_features() and enable hardware feature detection for x86_64. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- autogen.sh | 4 + conf/Makefile.extra-dist | 1 + .../libgcrypt-patches/13_add_hwfeatures.patch | 87 +++++++++++++++++++ include/grub/crypto.h | 7 +- 4 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 grub-core/lib/libgcrypt-patches/13_add_hwfeatures.patch diff --git a/autogen.sh b/autogen.sh index 7ff90cb93..7dd26cd33 100755 --- a/autogen.sh +++ b/autogen.sh @@ -59,6 +59,10 @@ for x in sha256-ssse3-amd64.S sha256-avx-amd64.S sha256-avx2-bmi2-amd64.S sha256 cp grub-core/lib/libgcrypt/cipher/"$x" grub-core/lib/libgcrypt-grub/cipher/"$x" done +if [ -f grub-core/lib/libgcrypt-grub/src/hwfeatures.c ]; then + rm grub-core/lib/libgcrypt-grub/src/hwfeatures.c +fi + for x in grub-core/lib/libgcrypt-patches/*.patch; do patch -i $x -p1 done diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index 48c2b75f8..7c5afe680 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -47,6 +47,7 @@ EXTRA_DIST += grub-core/lib/libgcrypt-patches/09-blake2b-hash-buffers.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/10-kdf-use-GPG-errs.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/11-kdf-remove-unsupported-kdfs.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/12-kdf-use-grub_divmod64.patch +EXTRA_DIST += grub-core/lib/libgcrypt-patches/13_add_hwfeatures.patch EXTRA_DIST += grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch EXTRA_DIST += grub-core/lib/libtasn1-patches/0002-libtasn1-replace-strcat-with-strcpy-in-_asn1_str_cat.patch diff --git a/grub-core/lib/libgcrypt-patches/13_add_hwfeatures.patch b/grub-core/lib/libgcrypt-patches/13_add_hwfeatures.patch new file mode 100644 index 000000000..1360b666e --- /dev/null +++ b/grub-core/lib/libgcrypt-patches/13_add_hwfeatures.patch @@ -0,0 +1,87 @@ +From 4403c452240417aaac7d3120c80b1325b7218768 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Fri, 18 Jul 2025 15:21:51 +0800 +Subject: [PATCH 1/4] libgcrypt: Implement _gcry_get_hw_features() + +Implement _gcry_get_hw_features() and enable hardware feature detection +for x86_64. + +Signed-off-by: Gary Lin +--- + grub-core/Makefile.gcry.def | 8 ++++ + grub-core/lib/libgcrypt-grub/src/hwfeatures.c | 47 +++++++++++++++++++ + 2 files changed, 55 insertions(+) + create mode 100644 grub-core/lib/libgcrypt-grub/src/hwfeatures.c + +diff --git a/grub-core/Makefile.gcry.def b/grub-core/Makefile.gcry.def +index a0593fa09..c8caf17dc 100644 +--- a/grub-core/Makefile.gcry.def ++++ b/grub-core/Makefile.gcry.def +@@ -226,3 +226,11 @@ module = { + cppflags = '$(CPPFLAGS_GCRY)'; + }; + ++module = { ++ name = gcry_hwfeatures; ++ common = lib/libgcrypt-grub/src/hwfeatures.c; ++ x86_64_efi = lib/libgcrypt-grub/src/hwf-x86.c; ++ ++ cflags = '$(CFLAGS_GCRY)'; ++ cppflags = '$(CPPFLAGS_GCRY) $(CPPFLAGS_GCRY_ASM)'; ++}; +diff --git a/grub-core/lib/libgcrypt-grub/src/hwfeatures.c b/grub-core/lib/libgcrypt-grub/src/hwfeatures.c +new file mode 100644 +index 000000000..4d744f8ec +--- /dev/null ++++ b/grub-core/lib/libgcrypt-grub/src/hwfeatures.c +@@ -0,0 +1,47 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2025 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++GRUB_MOD_LICENSE ("GPLv3+"); ++ ++#include ++#include ++#include "hwf-common.h" ++ ++unsigned int ++_gcry_get_hw_features (void) ++{ ++ static bool detected = false; ++ static unsigned int hw_features = 0; ++ ++ if (grub_gcry_hwf_enabled () == false) ++ return 0; ++ ++ if (detected == true) ++ return hw_features; ++ ++#if defined (__x86_64__) && defined (GRUB_MACHINE_EFI) ++ hw_features = _gcry_hwf_detect_x86 (); ++#endif ++ ++ grub_dprintf ("hwfeatures", "Detected features: 0x%x\n", hw_features); ++ ++ detected = true; ++ ++ return hw_features; ++} +-- +2.51.0 + diff --git a/include/grub/crypto.h b/include/grub/crypto.h index 68f5c10c3..f80ed25bb 100644 --- a/include/grub/crypto.h +++ b/include/grub/crypto.h @@ -626,11 +626,14 @@ _gcry_ct_memequal (const void *b1, const void *b2, grub_size_t len); unsigned int _gcry_ct_not_memequal (const void *b1, const void *b2, grub_size_t len); - -static inline unsigned int _gcry_get_hw_features(void) +#if defined (GRUB_UTIL) +static inline unsigned int _gcry_get_hw_features (void) { return 0; } +#else +extern unsigned int _gcry_get_hw_features (void); +#endif void *_gcry_malloc(grub_size_t n); void *_gcry_malloc_secure(grub_size_t n); From 2158d8e8a7f1b34e874200a82617e53874bad899 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 22 Oct 2025 09:28:57 +0800 Subject: [PATCH 090/208] libgcrypt: Declare the sha256 shaext function There is no prototype of _gcry_sha256_transform_intel_shaext() defined in the header or libgcrypt-grub/cipher/sha256.c, and gcc may complain the missing-prototypes error when compiling sha256-intel-shaext.c. Declare the prototype in sha256-intel-shaext.c to avoid the error. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- conf/Makefile.extra-dist | 1 + .../14_fix_build_shaext.patch | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 grub-core/lib/libgcrypt-patches/14_fix_build_shaext.patch diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index 7c5afe680..cebf780b5 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -48,6 +48,7 @@ EXTRA_DIST += grub-core/lib/libgcrypt-patches/10-kdf-use-GPG-errs.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/11-kdf-remove-unsupported-kdfs.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/12-kdf-use-grub_divmod64.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/13_add_hwfeatures.patch +EXTRA_DIST += grub-core/lib/libgcrypt-patches/14_fix_build_shaext.patch EXTRA_DIST += grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch EXTRA_DIST += grub-core/lib/libtasn1-patches/0002-libtasn1-replace-strcat-with-strcpy-in-_asn1_str_cat.patch diff --git a/grub-core/lib/libgcrypt-patches/14_fix_build_shaext.patch b/grub-core/lib/libgcrypt-patches/14_fix_build_shaext.patch new file mode 100644 index 000000000..b9d311465 --- /dev/null +++ b/grub-core/lib/libgcrypt-patches/14_fix_build_shaext.patch @@ -0,0 +1,35 @@ +From 5698f7c5055ea481d0040ea4495829e5d02781cc Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Fri, 18 Jul 2025 15:34:21 +0800 +Subject: [PATCH 2/4] libgcrypt: Declare the sha256 shaext function + +There is no prototype of _gcry_sha256_transform_intel_shaext() defined +in the header or libgcrypt-grub/cipher/sha256.c, and gcc may complain +the missing-prototypes error when compiling sha256-intel-shaext.c. + +Declare the prototype in sha256-intel-shaext.c to avoid the error. + +Signed-off-by: Gary Lin +--- + grub-core/lib/libgcrypt-grub/cipher/sha256-intel-shaext.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/grub-core/lib/libgcrypt-grub/cipher/sha256-intel-shaext.c b/grub-core/lib/libgcrypt-grub/cipher/sha256-intel-shaext.c +index 48c09eefe..7ec49f05e 100644 +--- a/grub-core/lib/libgcrypt-grub/cipher/sha256-intel-shaext.c ++++ b/grub-core/lib/libgcrypt-grub/cipher/sha256-intel-shaext.c +@@ -95,6 +95,11 @@ typedef struct u128_s + u32 a, b, c, d; + } u128_t; + ++ ++unsigned int ASM_FUNC_ATTR ++_gcry_sha256_transform_intel_shaext(u32 state[8], const unsigned char *data, ++ size_t nblks); ++ + /* + * Transform nblks*64 bytes (nblks*16 32-bit words) at DATA. + */ +-- +2.51.0 + From 70b2f5f08d9454bc7ecf9344ba090b449f2240da Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 22 Oct 2025 09:28:58 +0800 Subject: [PATCH 091/208] libgcrypt: Add hardware acceleration for gcry_sha256 Enable hardware acceleration for the gcry_sha256 module when building for the x86_64 EFI target. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- conf/Makefile.extra-dist | 1 + .../15_build_sha256_x86_64_efi_opt_code.patch | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 grub-core/lib/libgcrypt-patches/15_build_sha256_x86_64_efi_opt_code.patch diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index cebf780b5..f524f944b 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -49,6 +49,7 @@ EXTRA_DIST += grub-core/lib/libgcrypt-patches/11-kdf-remove-unsupported-kdfs.pat EXTRA_DIST += grub-core/lib/libgcrypt-patches/12-kdf-use-grub_divmod64.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/13_add_hwfeatures.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/14_fix_build_shaext.patch +EXTRA_DIST += grub-core/lib/libgcrypt-patches/15_build_sha256_x86_64_efi_opt_code.patch EXTRA_DIST += grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch EXTRA_DIST += grub-core/lib/libtasn1-patches/0002-libtasn1-replace-strcat-with-strcpy-in-_asn1_str_cat.patch diff --git a/grub-core/lib/libgcrypt-patches/15_build_sha256_x86_64_efi_opt_code.patch b/grub-core/lib/libgcrypt-patches/15_build_sha256_x86_64_efi_opt_code.patch new file mode 100644 index 000000000..8cab9b866 --- /dev/null +++ b/grub-core/lib/libgcrypt-patches/15_build_sha256_x86_64_efi_opt_code.patch @@ -0,0 +1,43 @@ +From 3443b213bb87112144d753678d0f1bbbc72b2b7a Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Fri, 18 Jul 2025 15:23:25 +0800 +Subject: [PATCH 3/4] libgcrypt: Add hardware acceleration for gcry_sha256 + +Enable hardware acceleration for the gcry_sha256 module when building +for the x86_64 EFI target. + +Signed-off-by: Gary Lin +--- + grub-core/Makefile.gcry.def | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/grub-core/Makefile.gcry.def b/grub-core/Makefile.gcry.def +index c8caf17dc..ac1d9a088 100644 +--- a/grub-core/Makefile.gcry.def ++++ b/grub-core/Makefile.gcry.def +@@ -17,6 +17,7 @@ module = { + module = { + name = gcry_blake2; + common = lib/libgcrypt-grub/cipher/blake2.c; ++ + cflags = '$(CFLAGS_GCRY)'; + cppflags = '$(CPPFLAGS_GCRY)'; + }; +@@ -172,8 +173,13 @@ module = { + module = { + name = gcry_sha256; + common = lib/libgcrypt-grub/cipher/sha256.c; ++ x86_64_efi = lib/libgcrypt-grub/cipher/sha256-ssse3-amd64.S; ++ x86_64_efi = lib/libgcrypt-grub/cipher/sha256-avx-amd64.S; ++ x86_64_efi = lib/libgcrypt-grub/cipher/sha256-avx2-bmi2-amd64.S; ++ x86_64_efi = lib/libgcrypt-grub/cipher/sha256-intel-shaext.c; ++ + cflags = '$(CFLAGS_GCRY) -Wno-cast-align'; +- cppflags = '$(CPPFLAGS_GCRY)'; ++ cppflags = '$(CPPFLAGS_GCRY) -DUSE_SHA256 $(CPPFLAGS_GCRY_ASM)'; + }; + + module = { +-- +2.51.0 + From f8f68f14ae540c7981f5ecbb9bbfa4dc76f45aeb Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 22 Oct 2025 09:28:59 +0800 Subject: [PATCH 092/208] libgcrypt: Add hardware acceleration for gcry_sha512 Enable hardware acceleration for the gcry_sha512 module when building for the x86_64 EFI target. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- conf/Makefile.extra-dist | 1 + .../16_build_sha512_x86_64_efi_opt_code.patch | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 grub-core/lib/libgcrypt-patches/16_build_sha512_x86_64_efi_opt_code.patch diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index f524f944b..846d45341 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -50,6 +50,7 @@ EXTRA_DIST += grub-core/lib/libgcrypt-patches/12-kdf-use-grub_divmod64.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/13_add_hwfeatures.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/14_fix_build_shaext.patch EXTRA_DIST += grub-core/lib/libgcrypt-patches/15_build_sha256_x86_64_efi_opt_code.patch +EXTRA_DIST += grub-core/lib/libgcrypt-patches/16_build_sha512_x86_64_efi_opt_code.patch EXTRA_DIST += grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch EXTRA_DIST += grub-core/lib/libtasn1-patches/0002-libtasn1-replace-strcat-with-strcpy-in-_asn1_str_cat.patch diff --git a/grub-core/lib/libgcrypt-patches/16_build_sha512_x86_64_efi_opt_code.patch b/grub-core/lib/libgcrypt-patches/16_build_sha512_x86_64_efi_opt_code.patch new file mode 100644 index 000000000..9cdde8830 --- /dev/null +++ b/grub-core/lib/libgcrypt-patches/16_build_sha512_x86_64_efi_opt_code.patch @@ -0,0 +1,35 @@ +From f62c2c7565237bbf059220e90d3f1f8c8d6eebd5 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Wed, 3 Sep 2025 16:26:55 +0800 +Subject: [PATCH 4/4] libgcrypt: Add hardware acceleration for gcry_sha512 + +Enable hardware acceleration for the gcry_sha512 module when building +for the x86_64 EFI target. + +Signed-off-by: Gary Lin +--- + grub-core/Makefile.gcry.def | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/grub-core/Makefile.gcry.def b/grub-core/Makefile.gcry.def +index ac1d9a088..64ea5ee24 100644 +--- a/grub-core/Makefile.gcry.def ++++ b/grub-core/Makefile.gcry.def +@@ -186,8 +186,13 @@ module = { + name = gcry_sha512; + common = lib/libgcrypt-grub/cipher/sha512.c; + common = lib/libgcrypt-grub/cipher/hash-common.c; ++ x86_64_efi = lib/libgcrypt-grub/cipher/sha512-ssse3-amd64.S; ++ x86_64_efi = lib/libgcrypt-grub/cipher/sha512-avx-amd64.S; ++ x86_64_efi = lib/libgcrypt-grub/cipher/sha512-avx2-bmi2-amd64.S; ++ x86_64_efi = lib/libgcrypt-grub/cipher/sha512-avx512-amd64.S; ++ + cflags = '$(CFLAGS_GCRY) -Wno-cast-align'; +- cppflags = '$(CPPFLAGS_GCRY)'; ++ cppflags = '$(CPPFLAGS_GCRY) -DUSE_SHA512 $(CPPFLAGS_GCRY_ASM)'; + }; + + module = { +-- +2.51.0 + From 91ddada642a5bf80df6f992b7e54b37b40c6a4ea Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Wed, 22 Oct 2025 09:29:00 +0800 Subject: [PATCH 093/208] disk/cryptodisk: Add --hw-accel to enable hardware acceleration The --hw-accel option has been added to cryptomount to speed up decryption by temporarily enabling hardware-specific instruction sets (e.g., AVX, SSE) in libgcrypt. A new feature, "feature_gcry_hw_accel", is also introduced to mark the availability of the new option. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- docs/grub.texi | 5 +++-- grub-core/disk/cryptodisk.c | 26 +++++++++++++++++++++++--- grub-core/normal/main.c | 3 ++- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index 52a98a97d..99f583f9b 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -7134,7 +7134,7 @@ The option @option{--quiet} can be given to suppress the output. @node cryptomount @subsection cryptomount -@deffn Command cryptomount [ [@option{-p} password] | [@option{-k} keyfile [@option{-O} keyoffset] [@option{-S} keysize] ] | [@option{-P} protector] ] [@option{-H} file] device|@option{-u} uuid|@option{-a}|@option{-b} +@deffn Command cryptomount [ [@option{-p} password] | [@option{-k} keyfile [@option{-O} keyoffset] [@option{-S} keysize] ] | [@option{-P} protector] | [@option{-A}] ] [@option{-H} file] device|@option{-u} uuid|@option{-a}|@option{-b} Setup access to encrypted device. A passphrase will be requested interactively, if neither the @option{-p} nor @option{-k} options are given. The option @option{-p} can be used to supply a passphrase (useful for scripts). @@ -7142,7 +7142,8 @@ Alternatively the @option{-k} option can be used to supply a keyfile with options @option{-O} and @option{-S} optionally supplying the offset and size, respectively, of the key data in the given key file. Besides the keyfile, the key can be stored in a key protector, and option @option{-P} configures -specific key protector, e.g. tpm2, to retrieve the key from. +specific key protector, e.g. tpm2, to retrieve the key from. The option @option{-A} +enables hardware acceleration in libgcrypt to speed up decryption. The @option{-H} options can be used to supply cryptomount backends with an alternative header file (aka detached header). Not all backends have headers nor support alternative header files (currently only LUKS1 and LUKS2 support them). diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 9af665df3..290821bb6 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -29,6 +29,7 @@ #include #include #include +#include #ifdef GRUB_UTIL #include @@ -48,7 +49,8 @@ enum OPTION_KEYFILE_OFFSET, OPTION_KEYFILE_SIZE, OPTION_HEADER, - OPTION_PROTECTOR + OPTION_PROTECTOR, + OPTION_HWACCEL }; static const struct grub_arg_option options[] = @@ -64,6 +66,7 @@ static const struct grub_arg_option options[] = {"header", 'H', 0, N_("Read header from file"), 0, ARG_TYPE_STRING}, {"protector", 'P', GRUB_ARG_OPTION_REPEATABLE, N_("Unlock volume(s) using key protector(s)."), 0, ARG_TYPE_STRING}, + {"hw-accel", 'A', 0, N_("Enable hardware acceleration."), 0, 0}, {0, 0, 0, 0, 0, 0} }; @@ -1420,7 +1423,7 @@ grub_cryptodisk_clear_key_cache (struct grub_cryptomount_args *cargs) } static grub_err_t -grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) +__grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) { struct grub_arg_list *state = ctxt->state; struct grub_cryptomount_args cargs = {0}; @@ -1629,6 +1632,23 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) } } +static grub_err_t +grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; + grub_err_t err; + + if (state[OPTION_HWACCEL].set) + grub_enable_gcry_hwf (); + + err = __grub_cmd_cryptomount (ctxt, argc, args); + + if (state[OPTION_HWACCEL].set) + grub_reset_gcry_hwf (); + + return err; +} + static struct grub_disk_dev grub_cryptodisk_dev = { .name = "cryptodisk", .id = GRUB_DISK_DEVICE_CRYPTODISK_ID, @@ -1898,7 +1918,7 @@ GRUB_MOD_INIT (cryptodisk) cmd = grub_register_extcmd ("cryptomount", grub_cmd_cryptomount, 0, N_("[ [-p password] | [-k keyfile" " [-O keyoffset] [-S keysize] ] ] [-H file]" - " [-P protector [-P protector ...]]" + " [-P protector [-P protector ...]] | [-A]" " "), N_("Mount a crypto device."), options); grub_procfs_register ("luks_script", &luks_script); diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 01b79ac32..8c2acf938 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -518,7 +518,8 @@ static const char *features[] = { "feature_default_font_path", "feature_all_video_module", "feature_menuentry_id", "feature_menuentry_options", "feature_200_final", "feature_nativedisk_cmd", "feature_timeout_style", - "feature_search_cryptodisk_only", "feature_tpm2_cap_pcrs" + "feature_search_cryptodisk_only", "feature_tpm2_cap_pcrs", + "feature_gcry_hw_accel" }; GRUB_MOD_INIT(normal) From 3c9762b12fa517a067719009b9c8c3be41d1cbdc Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Fri, 17 Oct 2025 17:01:32 +0800 Subject: [PATCH 094/208] kern/misc: Add the "z" length modifier support Add support for the "z" length modifier in the printf code. This allows printing of size_t and ssize_t values using %zu, %zd and related formats. The parser maps "z" to the correct integer width based on sizeof(size_t). Signed-off-by: Michael Chang Reviewed-by: Neal Gompa Reviewed-by: Daniel Kiper --- grub-core/kern/misc.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index 2faff00ea..18c34b8eb 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -26,6 +26,7 @@ #include #include #include +#include union printf_arg { @@ -832,6 +833,9 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args, COMPILE_TIME_ASSERT (sizeof (long) <= sizeof (long long)); COMPILE_TIME_ASSERT (sizeof (long long) == sizeof (void *) || sizeof (int) == sizeof (void *)); + COMPILE_TIME_ASSERT (sizeof (size_t) == sizeof (unsigned) + || sizeof (size_t) == sizeof (unsigned long) + || sizeof (size_t) == sizeof (unsigned long long)); fmt = fmt0; while ((c = *fmt++) != 0) @@ -866,11 +870,17 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args, fmt++; c = *fmt++; + if (c == 'z') + { + c = *fmt++; + goto do_count; + } if (c == 'l') c = *fmt++; if (c == 'l') c = *fmt++; + do_count: switch (c) { case 'p': @@ -967,6 +977,14 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args, continue; } + if (c == 'z') + { + c = *fmt++; + if (sizeof (size_t) == sizeof (unsigned long)) + longfmt = 1; + else if (sizeof (size_t) == sizeof (unsigned long long)) + longfmt = 2; + } if (c == 'l') { c = *fmt++; @@ -1143,6 +1161,8 @@ grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0, } c = *fmt++; + if (c == 'z') + c = *fmt++; if (c == 'l') c = *fmt++; if (c == 'l') From 512e33ec7c1ede5859251b3ec91ece8d8714f663 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Fri, 17 Oct 2025 17:01:33 +0800 Subject: [PATCH 095/208] tests: Add "z" length modifier printf tests Add unit tests for %zd, %zu and %zx to verify size_t and ssize_t formatting matches system snprintf(). Signed-off-by: Michael Chang Reviewed-by: Neal Gompa Reviewed-by: Daniel Kiper --- tests/printf_unit_test.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/printf_unit_test.c b/tests/printf_unit_test.c index 098c29fd9..f2de187b0 100644 --- a/tests/printf_unit_test.c +++ b/tests/printf_unit_test.c @@ -20,6 +20,7 @@ #include #include #include +#include #define MSG "printf test failed: %s, %s", real, expected @@ -73,6 +74,15 @@ printf_test (void) grub_snprintf (real, sizeof (real), "%%0%dd ", 1); snprintf (expected, sizeof (expected), "%%0%dd ", 1); grub_test_assert (strcmp (real, expected) == 0, MSG); + grub_snprintf (real, sizeof (real), "%zd %zd %zd", (ssize_t) -1, (ssize_t) (SIZE_MAX >> 1), (ssize_t) 42); + snprintf (expected, sizeof (expected), "%zd %zd %zd", (ssize_t) -1, (ssize_t) (SIZE_MAX >> 1), (ssize_t) 42); + grub_test_assert (strcmp (real, expected) == 0, MSG); + grub_snprintf (real, sizeof (real), "%zu %zu %zu", (size_t) 0, (size_t) SIZE_MAX, (size_t) 42); + snprintf (expected, sizeof (expected), "%zu %zu %zu", (size_t) 0, (size_t) SIZE_MAX, (size_t) 42); + grub_test_assert (strcmp (real, expected) == 0, MSG); + grub_snprintf (real, sizeof (real), "%zx %zx %zx", (ssize_t) (SIZE_MAX >> 1), (size_t) SIZE_MAX, (size_t) 0xdeadbeefU); + snprintf (expected, sizeof (expected), "%zx %zx %zx", (ssize_t) (SIZE_MAX >> 1), (size_t) SIZE_MAX, (size_t) 0xdeadbeefU); + grub_test_assert (strcmp (real, expected) == 0, MSG); } GRUB_UNIT_TEST ("printf_unit_test", printf_test); From e4d684cc4230f31addecacc72a2b9e421d01bc07 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Fri, 17 Oct 2025 17:01:34 +0800 Subject: [PATCH 096/208] util/grub-editenv: Add fs_envblk open helper This patch adds the logic to locate and open an environment block that is stored in a reserved area on the device. It introduces the function fs_envblk_open() together with helper routines to read the block pointed to by the env_block variable, and to create the block on disk when it does not exist yet. When a block is created, the code records its location inside the file based envblk by setting env_block in block list syntax of offset plus size in sectors. The env_block variable acts as a link from the file envblk to the raw disk region so that later runs of grub-editenv can follow it and access the external block. The helper is exposed through a small ops table attached to fs_envblk so that later patches can call fs_envblk->ops->open() without touching core code again. At this stage variables are still stored in the file envblk and no redirection has been applied. In relation to this, the fs_envblk_spec table defines the file-system specific layout of the reserved raw blocks used for environment storage. It is prepared to facilitate integration in grub-editenv, with Btrfs to be added in the future once its reserved area is defined. An fs_envblk_init() helper is added to prepare it for using the ops with its associated data context if the feature is available. It is not used yet, but will be used later when a filesystem and its device are probed to initialize the fs_envblk handle and enable access to the feature. Signed-off-by: Michael Chang Reviewed-by: Neal Gompa Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- util/grub-editenv.c | 170 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/util/grub-editenv.c b/util/grub-editenv.c index db6f187cc..7c0105188 100644 --- a/util/grub-editenv.c +++ b/util/grub-editenv.c @@ -120,6 +120,176 @@ block, use `rm %s'."), NULL, help_filter, NULL }; +struct fs_envblk_spec { + const char *fs_name; + off_t offset; + size_t size; +}; +typedef struct fs_envblk_spec fs_envblk_spec_t; + +static grub_envblk_t fs_envblk_open (grub_envblk_t envblk); + +struct fs_envblk_ops { + grub_envblk_t (*open) (grub_envblk_t); +}; +typedef struct fs_envblk_ops fs_envblk_ops_t; + +struct fs_envblk { + fs_envblk_spec_t *spec; + fs_envblk_ops_t *ops; + const char *dev; +}; +typedef struct fs_envblk *fs_envblk_t; + +static fs_envblk_ops_t fs_envblk_ops = { + .open = fs_envblk_open +}; + +/* + * fs_envblk_spec describes the file-system specific layout of reserved raw + * blocks used as environment blocks. + */ +static fs_envblk_spec_t fs_envblk_spec[] = { + { NULL, 0, 0 } +}; + +static fs_envblk_t fs_envblk = NULL; + +static void __attribute__ ((unused)) +fs_envblk_init (const char *fs_name, const char *dev) +{ + fs_envblk_spec_t *p; + + if (fs_name == NULL || dev == NULL) + return; + + for (p = fs_envblk_spec; p->fs_name != NULL; p++) + { + if (strcmp (fs_name, p->fs_name) == 0) + { + if (fs_envblk == NULL) + fs_envblk = xmalloc (sizeof (*fs_envblk)); + fs_envblk->spec = p; + fs_envblk->dev = xstrdup (dev); + fs_envblk->ops = &fs_envblk_ops; + break; + } + } +} + +static int +read_env_block_var (const char *varname, const char *value, void *hook_data) +{ + grub_envblk_t *p_envblk = (grub_envblk_t *) hook_data; + off_t off; + size_t sz; + char *p, *buf; + FILE *fp; + + if (p_envblk == NULL || fs_envblk == NULL) + return 1; + + if (strcmp (varname, "env_block") != 0) + return 0; + + off = strtol (value, &p, 10); + if (*p == '+') + sz = strtol (p + 1, &p, 10); + else + return 0; + + if (*p != '\0' || sz == 0) + return 0; + + off <<= GRUB_DISK_SECTOR_BITS; + sz <<= GRUB_DISK_SECTOR_BITS; + + fp = grub_util_fopen (fs_envblk->dev, "rb"); + if (fp == NULL) + grub_util_error (_("cannot open `%s': %s"), fs_envblk->dev, strerror (errno)); + + if (fseek (fp, off, SEEK_SET) < 0) + grub_util_error (_("cannot seek `%s': %s"), fs_envblk->dev, strerror (errno)); + + buf = xmalloc (sz); + if ((fread (buf, 1, sz, fp)) != sz) + grub_util_error (_("cannot read `%s': %s"), fs_envblk->dev, strerror (errno)); + + fclose (fp); + + *p_envblk = grub_envblk_open (buf, sz); + + return 1; +} + +static void +create_env_on_block (void) +{ + FILE *fp; + char *buf; + const char *device; + off_t offset; + size_t size; + + if (fs_envblk == NULL) + return; + + device = fs_envblk->dev; + offset = fs_envblk->spec->offset; + size = fs_envblk->spec->size; + + fp = grub_util_fopen (device, "r+b"); + if (fp == NULL) + grub_util_error (_("cannot open `%s': %s"), device, strerror (errno)); + + buf = xmalloc (size); + memcpy (buf, GRUB_ENVBLK_SIGNATURE, sizeof (GRUB_ENVBLK_SIGNATURE) - 1); + memset (buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1, '#', size - sizeof (GRUB_ENVBLK_SIGNATURE) + 1); + + if (fseek (fp, offset, SEEK_SET) < 0) + grub_util_error (_("cannot seek `%s': %s"), device, strerror (errno)); + + if (fwrite (buf, 1, size, fp) != size) + grub_util_error (_("cannot write to `%s': %s"), device, strerror (errno)); + + grub_util_file_sync (fp); + free (buf); + fclose (fp); +} + +static grub_envblk_t +fs_envblk_open (grub_envblk_t envblk) +{ + grub_envblk_t envblk_on_block = NULL; + char *val; + off_t offset; + size_t size; + + if (envblk == NULL) + return NULL; + + offset = fs_envblk->spec->offset; + size = fs_envblk->spec->size; + + grub_envblk_iterate (envblk, &envblk_on_block, read_env_block_var); + + if (envblk_on_block != NULL && grub_envblk_size (envblk_on_block) == size) + return envblk_on_block; + + create_env_on_block (); + + offset = offset >> GRUB_DISK_SECTOR_BITS; + size = (size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS; + + val = xasprintf ("%lld+%zu", (long long) offset, size); + if (grub_envblk_set (envblk, "env_block", val) == 0) + grub_util_error ("%s", _("environment block too small")); + grub_envblk_iterate (envblk, &envblk_on_block, read_env_block_var); + free (val); + + return envblk_on_block; +} + static grub_envblk_t open_envblk_file (const char *name) { From 2abdd8cd21c93de45d0ee4a3f6d68910fa6b2cb3 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Fri, 17 Oct 2025 17:01:35 +0800 Subject: [PATCH 097/208] util/grub-editenv: Add fs_envblk write helper This patch adds the function fs_envblk_write to update the reserved environment block on disk. The helper takes an in memory envblk buffer and writes it back to the device at the location defined by the fs_envblk specification. It performs size checks and uses file sync to ensure that the updated data is flushed. The helper is also added into the fs_envblk ops table, together with the open helper from the previous patch. With this change the basic input and output path for an external environment block is complete. The choice of which variables should be written externally will be handled by later patches. Signed-off-by: Michael Chang Reviewed-by: Neal Gompa Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- util/grub-editenv.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/util/grub-editenv.c b/util/grub-editenv.c index 7c0105188..c1a8a5117 100644 --- a/util/grub-editenv.c +++ b/util/grub-editenv.c @@ -128,9 +128,11 @@ struct fs_envblk_spec { typedef struct fs_envblk_spec fs_envblk_spec_t; static grub_envblk_t fs_envblk_open (grub_envblk_t envblk); +static void fs_envblk_write (grub_envblk_t envblk); struct fs_envblk_ops { grub_envblk_t (*open) (grub_envblk_t); + void (*write) (grub_envblk_t); }; typedef struct fs_envblk_ops fs_envblk_ops_t; @@ -142,7 +144,8 @@ struct fs_envblk { typedef struct fs_envblk *fs_envblk_t; static fs_envblk_ops_t fs_envblk_ops = { - .open = fs_envblk_open + .open = fs_envblk_open, + .write = fs_envblk_write }; /* @@ -378,6 +381,39 @@ write_envblk (const char *name, grub_envblk_t envblk) fclose (fp); } +static void +fs_envblk_write (grub_envblk_t envblk) +{ + FILE *fp; + const char *device; + off_t offset; + size_t size; + + if (envblk == NULL) + return; + + device = fs_envblk->dev; + offset = fs_envblk->spec->offset; + size = fs_envblk->spec->size; + + if (grub_envblk_size (envblk) > size) + grub_util_error ("%s", _("environment block too small")); + + fp = grub_util_fopen (device, "r+b"); + + if (fp == NULL) + grub_util_error (_("cannot open `%s': %s"), device, strerror (errno)); + + if (fseek (fp, offset, SEEK_SET) < 0) + grub_util_error (_("cannot seek `%s': %s"), device, strerror (errno)); + + if (fwrite (grub_envblk_buffer (envblk), 1, grub_envblk_size (envblk), fp) != grub_envblk_size (envblk)) + grub_util_error (_("cannot write to `%s': %s"), device, strerror (errno)); + + grub_util_file_sync (fp); + fclose (fp); +} + static void set_variables (const char *name, int argc, char *argv[]) { From c7c901916564d4288a1b4e763705e80a164bf02a Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Fri, 17 Oct 2025 17:01:36 +0800 Subject: [PATCH 098/208] util/grub-editenv: Wire set_variables() to optional fs_envblk This patch changes set_variables() so that it can use an external environment block when one is present. The variable next_entry is written into the external block, env_block is treated as read only, and all other variables are written into the normal file based envblk. A cleanup step is added to handle cases where GRUB at runtime writes variables into the external block because file based updates are not safe on a copy on write filesystem such as Btrfs. For example, the savedefault command can update saved_entry, and on Btrfs GRUB will place that update in the external block instead of the file envblk. If an older copy remains in the external block, it would override the newer value from the file envblk when GRUB first loads the file and then applies the external block on top of it. To avoid this, whenever a variable is updated in the file envblk, any same named key in the external block is deleted. Signed-off-by: Michael Chang Reviewed-by: Neal Gompa Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- util/grub-editenv.c | 58 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/util/grub-editenv.c b/util/grub-editenv.c index c1a8a5117..7d4981d67 100644 --- a/util/grub-editenv.c +++ b/util/grub-editenv.c @@ -414,12 +414,35 @@ fs_envblk_write (grub_envblk_t envblk) fclose (fp); } +struct var_lookup_ctx { + const char *varname; + bool found; +}; +typedef struct var_lookup_ctx var_lookup_ctx_t; + +static int +var_lookup_iter (const char *varname, const char *value __attribute__ ((unused)), void *hook_data) +{ + var_lookup_ctx_t *ctx = (var_lookup_ctx_t *) hook_data; + + if (grub_strcmp (ctx->varname, varname) == 0) + { + ctx->found = true; + return 1; + } + return 0; +} + static void set_variables (const char *name, int argc, char *argv[]) { grub_envblk_t envblk; + grub_envblk_t envblk_on_block = NULL; envblk = open_envblk_file (name); + if (fs_envblk != NULL) + envblk_on_block = fs_envblk->ops->open (envblk); + while (argc) { char *p; @@ -430,15 +453,46 @@ set_variables (const char *name, int argc, char *argv[]) *(p++) = 0; - if (! grub_envblk_set (envblk, argv[0], p)) - grub_util_error ("%s", _("environment block too small")); + if ((strcmp (argv[0], "next_entry") == 0) && envblk_on_block != NULL) + { + if (grub_envblk_set (envblk_on_block, argv[0], p) == 0) + grub_util_error ("%s", _("environment block too small")); + goto next; + } + if (strcmp (argv[0], "env_block") == 0) + { + grub_util_warn (_("can't set env_block as it's read-only")); + goto next; + } + + if (grub_envblk_set (envblk, argv[0], p) == 0) + grub_util_error ("%s", _("environment block too small")); + + if (envblk_on_block != NULL) + { + var_lookup_ctx_t ctx = { + .varname = argv[0], + .found = false + }; + + grub_envblk_iterate (envblk_on_block, &ctx, var_lookup_iter); + if (ctx.found == true) + grub_envblk_delete (envblk_on_block, argv[0]); + } + next: argc--; argv++; } write_envblk (name, envblk); grub_envblk_close (envblk); + + if (envblk_on_block != NULL) + { + fs_envblk->ops->write (envblk_on_block); + grub_envblk_close (envblk_on_block); + } } static void From 84e2bc2f1e69253f9f0d4ca1891238c4d8374be1 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Fri, 17 Oct 2025 17:01:37 +0800 Subject: [PATCH 099/208] util/grub-editenv: Wire unset_variables() to optional fs_envblk This patch updates unset_variables() so that removals are also applied to the external environment block when it is present. The code opens the external block, deletes the same named keys there, and then writes the external block back using fs_envblk_write(). The file based envblk is still updated and written as before. Signed-off-by: Michael Chang Reviewed-by: Neal Gompa Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- util/grub-editenv.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/util/grub-editenv.c b/util/grub-editenv.c index 7d4981d67..7f39ff120 100644 --- a/util/grub-editenv.c +++ b/util/grub-editenv.c @@ -499,18 +499,32 @@ static void unset_variables (const char *name, int argc, char *argv[]) { grub_envblk_t envblk; + grub_envblk_t envblk_on_block = NULL; envblk = open_envblk_file (name); + + if (fs_envblk != NULL) + envblk_on_block = fs_envblk->ops->open (envblk); + while (argc) { grub_envblk_delete (envblk, argv[0]); + if (envblk_on_block != NULL) + grub_envblk_delete (envblk_on_block, argv[0]); + argc--; argv++; } write_envblk (name, envblk); grub_envblk_close (envblk); + + if (envblk_on_block != NULL) + { + fs_envblk->ops->write (envblk_on_block); + grub_envblk_close (envblk_on_block); + } } int From 4b5ea8dca62fa98fa277f0ee3b3a47efbedb3f47 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Fri, 17 Oct 2025 17:01:38 +0800 Subject: [PATCH 100/208] util/grub-editenv: Wire list_variables() to optional fs_envblk This patch updates list_variables() so that it also prints entries from the external environment block when one is present. The function first lists all variables from the file based envblk, then iterates over the external envblk and prints those as well. The output format remains the same as before. The change makes it possible to inspect variables regardless of whether they are stored in the file envblk or in the reserved block. Signed-off-by: Michael Chang Reviewed-by: Neal Gompa Reviewed-by: Avnish Chouhan Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- util/grub-editenv.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/util/grub-editenv.c b/util/grub-editenv.c index 7f39ff120..f8fa294d8 100644 --- a/util/grub-editenv.c +++ b/util/grub-editenv.c @@ -355,10 +355,17 @@ static void list_variables (const char *name) { grub_envblk_t envblk; + grub_envblk_t envblk_on_block = NULL; envblk = open_envblk_file (name); + grub_envblk_iterate (envblk, &envblk_on_block, read_env_block_var); grub_envblk_iterate (envblk, NULL, print_var); grub_envblk_close (envblk); + if (envblk_on_block != NULL) + { + grub_envblk_iterate (envblk_on_block, NULL, print_var); + grub_envblk_close (envblk_on_block); + } } static void From d6525f0e65332555e0c65d4846050a45ea51969e Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Fri, 17 Oct 2025 17:01:39 +0800 Subject: [PATCH 101/208] util/grub-editenv: Add probe call for external envblk This patch adds the probe_fs_envblk() function to identify the root filesystem and invoke fs_envblk_init() with the probed filesystem type and device. This checks if the feature is available and initializes the handle, fs_envblk, to access the external environment block. It avoids configurations with diskfilter or cryptodisk where filesystem blocks may be remapped or encrypted. The probe is only invoked when grub-editenv is working on the default environment file path. This restriction ensures that probing and possible raw device access are not triggered for arbitrary user supplied paths, but only for the standard grubenv file. In that case the code checks if the filename equals DEFAULT_ENVBLK_PATH and then calls probe_fs_envblk with fs_envblk_spec. The result is stored in the global fs_envblk handle. At this stage the external environment block is only detected and recorded, and the behavior of grub-editenv is unchanged. Signed-off-by: Michael Chang Reviewed-by: Neal Gompa Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- util/grub-editenv.c | 115 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 2 deletions(-) diff --git a/util/grub-editenv.c b/util/grub-editenv.c index f8fa294d8..1b923bf97 100644 --- a/util/grub-editenv.c +++ b/util/grub-editenv.c @@ -23,8 +23,11 @@ #include #include #include -#include +#include #include +#include +#include +#include #include #include @@ -158,7 +161,7 @@ static fs_envblk_spec_t fs_envblk_spec[] = { static fs_envblk_t fs_envblk = NULL; -static void __attribute__ ((unused)) +static void fs_envblk_init (const char *fs_name, const char *dev) { fs_envblk_spec_t *p; @@ -534,6 +537,111 @@ unset_variables (const char *name, int argc, char *argv[]) } } +static bool +is_abstraction (grub_device_t dev) +{ + if (dev == NULL || dev->disk == NULL) + return false; + + if (dev->disk->dev->id == GRUB_DISK_DEVICE_DISKFILTER_ID || + dev->disk->dev->id == GRUB_DISK_DEVICE_CRYPTODISK_ID) + return true; + + return false; +} + +static void +probe_fs_envblk (fs_envblk_spec_t *spec) +{ + char **grub_devices = NULL; + char **curdev, **curdrive; + size_t ndev = 0; + char **grub_drives = NULL; + grub_device_t grub_dev = NULL; + grub_fs_t grub_fs = NULL; + bool have_abstraction = false; + + grub_util_biosdisk_init (DEFAULT_DEVICE_MAP); + grub_init_all (); + grub_gcry_init_all (); + + grub_lvm_fini (); + grub_mdraid09_fini (); + grub_mdraid1x_fini (); + grub_diskfilter_fini (); + grub_diskfilter_init (); + grub_mdraid09_init (); + grub_mdraid1x_init (); + grub_lvm_init (); + + grub_devices = grub_guess_root_devices (DEFAULT_DIRECTORY); + + if (grub_devices == NULL || grub_devices[0] == NULL) + { + grub_util_warn (_("cannot find a device for %s (is /dev mounted?)"), DEFAULT_DIRECTORY); + goto cleanup; + } + + for (curdev = grub_devices; *curdev != NULL; curdev++, ndev++) + grub_util_pull_device (*curdev); + + grub_drives = xcalloc ((ndev + 1), sizeof (grub_drives[0])); + + for (curdev = grub_devices, curdrive = grub_drives; *curdev != NULL; curdev++, + curdrive++) + { + *curdrive = grub_util_get_grub_dev (*curdev); + if (*curdrive == NULL) + { + grub_util_warn (_("cannot find a GRUB drive for %s. Check your device.map"), + *curdev); + goto cleanup; + } + } + *curdrive = NULL; + + grub_dev = grub_device_open (grub_drives[0]); + if (grub_dev == NULL) + { + grub_util_warn (_("cannot open device %s: %s"), grub_drives[0], grub_errmsg); + grub_errno = GRUB_ERR_NONE; + goto cleanup; + } + + grub_fs = grub_fs_probe (grub_dev); + if (grub_fs == NULL) + { + grub_util_warn (_("cannot probe fs for %s: %s"), grub_drives[0], grub_errmsg); + grub_errno = GRUB_ERR_NONE; + goto cleanup; + } + + have_abstraction = is_abstraction (grub_dev); + for (curdrive = grub_drives + 1; *curdrive != NULL && have_abstraction == false; curdrive++) + { + grub_device_t dev = grub_device_open (*curdrive); + + if (dev == NULL) + continue; + have_abstraction = is_abstraction (dev); + grub_device_close (dev); + } + + if (have_abstraction == false) + fs_envblk_init (grub_fs->name, grub_devices[0]); + + cleanup: + if (grub_devices != NULL) + for (curdev = grub_devices; *curdev != NULL; curdev++) + free (*curdev); + free (grub_devices); + free (grub_drives); + grub_device_close (grub_dev); + grub_gcry_fini_all (); + grub_fini_all (); + grub_util_biosdisk_fini (); +} + int main (int argc, char *argv[]) { @@ -565,6 +673,9 @@ main (int argc, char *argv[]) command = argv[curindex++]; } + if (strcmp (filename, DEFAULT_ENVBLK_PATH) == 0) + probe_fs_envblk (fs_envblk_spec); + if (strcmp (command, "create") == 0) grub_util_create_envblk_file (filename); else if (strcmp (command, "list") == 0) From e4e1776613b446df510a0424fe2819333e1ea0df Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Fri, 17 Oct 2025 17:01:40 +0800 Subject: [PATCH 102/208] fs/btrfs: Add environment block to reserved header area This patch reserves space for the GRUB environment block inside the Btrfs header. The block is placed at an offset of GRUB_ENV_BTRFS_OFFSET, 256 KiB from the start of the device, and occupies one sector. To protect the space, overflow guard sectors are placed before and after the reserved block. The Btrfs header already defines regions for bootloader use. By adding this entry, GRUB gains a fixed and safe location to store the environment block without conflicting with other structures in the header. Add Btrfs and its reserved area information to the fs_envblk_spec table. With the groundworks done in previous patches, the function is now complete and working in grub-editenv. Signed-off-by: Michael Chang Reviewed-by: Neal Gompa Reviewed-by: Daniel Kiper --- grub-core/fs/btrfs.c | 10 +++++++++- include/grub/fs.h | 2 ++ util/grub-editenv.c | 9 ++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 7bf8d922f..fa5d860eb 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -2337,11 +2337,16 @@ struct embed_region { * https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#BOOTLOADER_SUPPORT * The first 1 MiB on each device is unused with the exception of primary * superblock that is on the offset 64 KiB and spans 4 KiB. + * + * Note: If this table is modified, also update + * util/grub-editenv.c::fs_envblk_spec, which describes the file-system + * specific layout of reserved raw blocks used as environment blocks so that + * both stay consistent. */ static const struct { struct embed_region available; - struct embed_region used[6]; + struct embed_region used[9]; } btrfs_head = { .available = {0, GRUB_DISK_KiB_TO_SECTORS (1024)}, /* The first 1 MiB. */ .used = { @@ -2349,6 +2354,9 @@ static const struct { {GRUB_DISK_KiB_TO_SECTORS (64) - 1, 1}, /* Overflow guard. */ {GRUB_DISK_KiB_TO_SECTORS (64), GRUB_DISK_KiB_TO_SECTORS (4)}, /* 4 KiB superblock. */ {GRUB_DISK_KiB_TO_SECTORS (68), 1}, /* Overflow guard. */ + {(GRUB_ENV_BTRFS_OFFSET >> GRUB_DISK_SECTOR_BITS) - 1, 1}, /* Overflow guard. */ + {(GRUB_ENV_BTRFS_OFFSET >> GRUB_DISK_SECTOR_BITS), 1}, /* Environment Block. */ + {(GRUB_ENV_BTRFS_OFFSET >> GRUB_DISK_SECTOR_BITS) + 1, 1}, /* Overflow guard. */ {GRUB_DISK_KiB_TO_SECTORS (1024) - 1, 1}, /* Overflow guard. */ {0, 0} /* Array terminator. */ } diff --git a/include/grub/fs.h b/include/grub/fs.h index df4c93b16..89e4d2b9b 100644 --- a/include/grub/fs.h +++ b/include/grub/fs.h @@ -132,4 +132,6 @@ grub_fs_unregister (grub_fs_t fs) grub_fs_t EXPORT_FUNC(grub_fs_probe) (grub_device_t device); +#define GRUB_ENV_BTRFS_OFFSET (256 * 1024) + #endif /* ! GRUB_FS_HEADER */ diff --git a/util/grub-editenv.c b/util/grub-editenv.c index 1b923bf97..41bb86739 100644 --- a/util/grub-editenv.c +++ b/util/grub-editenv.c @@ -153,9 +153,16 @@ static fs_envblk_ops_t fs_envblk_ops = { /* * fs_envblk_spec describes the file-system specific layout of reserved raw - * blocks used as environment blocks. + * blocks used as environment blocks. At present only Btrfs is supported. Other + * file-systems may be added if they provide a similar facility and avoid the + * limitation of writing to COW. + * + * Note: If this table is modified, also update + * grub-core/fs/btrfs.c::btrfs_head, which defines the layout in the Btrfs + * header and exports GRUB_ENV_BTRFS_OFFSET, so that both stay consistent. */ static fs_envblk_spec_t fs_envblk_spec[] = { + { "btrfs", GRUB_ENV_BTRFS_OFFSET, GRUB_DISK_SECTOR_SIZE }, { NULL, 0, 0 } }; From b63447748140845cce7ce05250193a514c322f26 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Fri, 17 Oct 2025 17:01:41 +0800 Subject: [PATCH 103/208] util/grub.d/00_header.in: Wire grub.cfg to use env_block when present This patch extends the generated grub.cfg so that it can use the external environment block when the variable env_block is defined. During boot, if env_block is set, grub.cfg builds a device path for it, exports the variable, and then loads its contents in addition to the normal grubenv file. When GRUB writes variables such as next_entry or saved_entry, the save commands are changed to write into env_block if it is set, and to fall back to the grubenv file otherwise. In this way the external environment block is used automatically, and existing commands like savedefault or save_env do not need to change. Signed-off-by: Michael Chang Reviewed-by: Neal Gompa Reviewed-by: Daniel Kiper --- util/grub.d/00_header.in | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in index f86b69bad..9d36feda3 100644 --- a/util/grub.d/00_header.in +++ b/util/grub.d/00_header.in @@ -46,6 +46,13 @@ cat << EOF if [ -s \$prefix/grubenv ]; then load_env fi + +if [ "\${env_block}" ] ; then + set env_block="(\${root})\${env_block}" + export env_block + load_env -f "\${env_block}" +fi + EOF if [ "x$GRUB_BUTTON_CMOS_ADDRESS" != "x" ]; then cat < Date: Fri, 17 Oct 2025 17:01:42 +0800 Subject: [PATCH 104/208] docs: Add Btrfs env block and special env vars Update grub.texi to describe the external environment block in the reserved area of Btrfs header used for grub-reboot and savedefault, and add a section documenting the saved_entry, next_entry, and env_block variables. Signed-off-by: Michael Chang Reviewed-by: Neal Gompa Reviewed-by: Daniel Kiper --- docs/grub.texi | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/docs/grub.texi b/docs/grub.texi index 99f583f9b..642dd4c67 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -3271,6 +3271,7 @@ GRUB. Others may be used freely in GRUB configuration files. @menu * Special environment variables:: * Environment block:: +* Special environment block variables:: * Passing environment variables through Xen:: @end menu @@ -3904,9 +3905,68 @@ For safety reasons, this storage is only available when installed on a plain disk (no LVM or RAID), using a non-checksumming filesystem (no ZFS), and using BIOS or EFI functions (no ATA, USB or IEEE1275). +On Btrfs filesystems, a reserved area in the filesystem header may be used to +store the environment block. This static block avoids the problems of updating +a normal file on a copy-on-write filesystem, where writing raw block is not +stable and requires metadata update. The reserved area provides a fixed +location that GRUB can update directly, allowing commands such as +@command{grub-reboot} and @samp{GRUB_SAVEDEFAULT} to function correctly on +Btrfs volumes. + @command{grub-mkconfig} uses this facility to implement @samp{GRUB_SAVEDEFAULT} (@pxref{Simple configuration}). + +@node Special environment block variables +@section Special environment block variables + +These special variables are usually written to the environment block +(@pxref{Environment block}) to customize the behavior of @file{grub.cfg} +generated by @command{grub-mkconfig}. + +@menu +* saved_entry:: +* next_entry:: +* env_block:: +@end menu + + +@node saved_entry +@subsection saved_entry + +The @var{saved_entry} variable sets the default boot entry in @file{grub.cfg} +created by @command{grub-mkconfig}. It can be set with +@command{grub-set-default} to choose a default entry, or at runtime with the +@code{savedefault} function in grub.cfg to save the current entry as the new +default. This may require write access by GRUB. + + +@node next_entry +@subsection next_entry + +The @var{next_entry} variable sets the boot entry for the next boot only. After +it is used, GRUB clears the value so it is not reused. This requires write +access to the environment block (@pxref{Environment block}) at runtime. The +@command{grub-reboot} command is usually used instead of changing this variable +directly. + + +@node env_block +@subsection env_block + +If the filesystem is Btrfs and the disk is not an abstracted device such as +LVM, RAID, or encryption, the reserved space in the Btrfs header can be used as +the environment block (@pxref{Environment block}). This provides a fixed raw +block that GRUB can reliably write to. The @var{env_block} records this +location in GRUB blocklist syntax (@pxref{Block list syntax}) so that +@command{grub-editenv} and @file{grub.cfg} know how to access and use the +external raw block. + +This variable is initialized when @file{grubenv} is first created by +@command{grub-editenv} and is treated as read-only to avoid being overwritten +with an unpredictable value. + + @node Passing environment variables through Xen @section Passing environment variables through Xen From 7c22c1000c815b396424e90c9f29e9b20b81450b Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Fri, 17 Oct 2025 17:01:43 +0800 Subject: [PATCH 105/208] fs/btrfs: Update doc link for bootloader support The old wiki link is obsolete and no longer updated. Change it to the current documentation. Signed-off-by: Michael Chang Reviewed-by: Neal Gompa Reviewed-by: Daniel Kiper --- grub-core/fs/btrfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index fa5d860eb..61dd1676a 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -2334,7 +2334,8 @@ struct embed_region { }; /* - * https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#BOOTLOADER_SUPPORT + * https://btrfs.readthedocs.io/en/latest/btrfs-man5.html#man-btrfs5-bootloader-support + * or invoke "man 5 btrfs" and visit the "BOOTLOADER SUPPORT" subsection. * The first 1 MiB on each device is unused with the exception of primary * superblock that is on the offset 64 KiB and spans 4 KiB. * From 8ea83c3ee247d3febc51729115780562d695d413 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Tue, 21 Oct 2025 09:56:25 -0600 Subject: [PATCH 106/208] io/zstdio: Implement zstdio decompression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add zstd based io decompression. Based largely on the existing xzio, implement the same features using the zstd library already included in the project. Signed-off-by: Logan Gunthorpe Reviewed-by: Avnish Chouhan  Reviewed-by: Daniel Kiper --- Makefile.util.def | 1 + grub-core/Makefile.core.def | 7 + grub-core/io/zstdio.c | 251 ++++++++++++++++++++++++++++++++++++ include/grub/file.h | 3 +- 4 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 grub-core/io/zstdio.c diff --git a/Makefile.util.def b/Makefile.util.def index 0196911e6..91720e627 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -164,6 +164,7 @@ library = { common = grub-core/io/gzio.c; common = grub-core/io/xzio.c; common = grub-core/io/lzopio.c; + common = grub-core/io/zstdio.c; common = grub-core/kern/ia64/dl_helper.c; common = grub-core/kern/arm/dl_helper.c; common = grub-core/kern/arm64/dl_helper.c; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 3d4b1a3b4..fa4bc54aa 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2524,6 +2524,13 @@ module = { cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/minilzo -DMINILZO_HAVE_CONFIG_H'; }; +module = { + name = zstdio; + common = io/zstdio.c; + cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/zstd'; + cflags='-Wno-unreachable-code'; +}; + module = { name = testload; common = commands/testload.c; diff --git a/grub-core/io/zstdio.c b/grub-core/io/zstdio.c new file mode 100644 index 000000000..379eba0c6 --- /dev/null +++ b/grub-core/io/zstdio.c @@ -0,0 +1,251 @@ +/* zstdio.c - decompression support for zstd */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2025 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#include "zstd.h" + +#define STREAM_HEADER_SIZE 16 + +struct zstdio +{ + grub_file_t file; + ZSTD_DCtx *dctx; + grub_size_t insize; + grub_size_t outsize; + + ZSTD_outBuffer output; + ZSTD_inBuffer input; + + grub_off_t saved_offset; + grub_uint8_t bufs[]; +}; +typedef struct zstdio *zstdio_t; + +static struct grub_fs zstdio_fs; + +static bool +test_header (grub_file_t file) +{ + zstdio_t zstdio = file->data; + size_t zret; + + zstdio->input.pos = 0; + zstdio->output.pos = 0; + zstdio->output.size = zstdio->outsize; + zstdio->input.size = grub_file_read (zstdio->file, zstdio->bufs, + STREAM_HEADER_SIZE); + if (zstdio->input.size != STREAM_HEADER_SIZE) + return false; + + zret = ZSTD_decompressStream (zstdio->dctx, &zstdio->output, &zstdio->input); + if (ZSTD_isError (zret)) + return false; + + return true; +} + +static grub_file_t +grub_zstdio_open (grub_file_t io, enum grub_file_type type) +{ + grub_file_t file; + zstdio_t zstdio; + + if (type & GRUB_FILE_TYPE_NO_DECOMPRESS) + return io; + + file = (grub_file_t) grub_zalloc (sizeof (*file)); + if (file == NULL) + return NULL; + + zstdio = grub_zalloc (sizeof (zstdio_t) + ZSTD_DStreamInSize () + + ZSTD_DStreamOutSize ()); + if (zstdio == NULL) + { + grub_free (file); + return NULL; + } + + zstdio->file = io; + zstdio->insize = ZSTD_DStreamInSize (); + zstdio->outsize = ZSTD_DStreamOutSize (); + zstdio->input.src = zstdio->bufs; + zstdio->output.dst = &zstdio->bufs[zstdio->insize]; + + file->device = io->device; + file->data = zstdio; + file->fs = &zstdio_fs; + file->size = GRUB_FILE_SIZE_UNKNOWN; + file->not_easily_seekable = 1; + + if (grub_file_tell (zstdio->file) != 0) + if (grub_file_seek (zstdio->file, 0) == (grub_off_t) -1) + { + grub_free (file); + grub_free (zstdio); + return NULL; + } + + zstdio->dctx = ZSTD_createDCtx (); + if (zstdio->dctx == NULL) + { + grub_free (file); + grub_free (zstdio); + return NULL; + } + + if (test_header (file) == false) + { + grub_errno = GRUB_ERR_NONE; + if (grub_file_seek (io, 0) == (grub_off_t) -1) + { + grub_free (file); + grub_free (zstdio); + return NULL; + } + + ZSTD_freeDCtx (zstdio->dctx); + grub_free (zstdio); + grub_free (file); + + return io; + } + + return file; +} + +static grub_ssize_t +grub_zstdio_read (grub_file_t file, char *buf, grub_size_t len) +{ + zstdio_t zstdio = file->data; + grub_ssize_t ret = 0; + grub_ssize_t readret; + grub_off_t current_offset; + grub_off_t new_offset; + grub_size_t delta; + grub_size_t zret; + + /* If seek backward need to reset decoder and start from beginning of file. */ + if (file->offset < zstdio->saved_offset) + { + ZSTD_initDStream (zstdio->dctx); + zstdio->input.pos = 0; + zstdio->input.size = 0; + zstdio->output.pos = 0; + zstdio->saved_offset = 0; + grub_file_seek (zstdio->file, 0); + } + + current_offset = zstdio->saved_offset; + + while (len > 0) + { + zstdio->output.size = file->offset + ret + len - current_offset; + if (zstdio->output.size > zstdio->outsize) + zstdio->output.size = zstdio->outsize; + if (zstdio->input.pos == zstdio->input.size) + { + readret = grub_file_read (zstdio->file, zstdio->bufs, + zstdio->insize); + if (readret < 0) + return -1; + + zstdio->input.size = readret; + zstdio->input.pos = 0; + } + + zret = ZSTD_decompressStream (zstdio->dctx, &zstdio->output, + &zstdio->input); + if (ZSTD_isError (zret)) + { + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, + N_("zstd file corrupted or unsupported block options")); + return -1; + } + + new_offset = current_offset + zstdio->output.pos; + + /* Store first chunk of data in buffer. */ + if (file->offset <= new_offset) + { + delta = new_offset - (file->offset + ret); + grub_memmove (buf, (grub_uint8_t *) zstdio->output.dst + + (zstdio->output.pos - delta), + delta); + len -= delta; + buf += delta; + ret += delta; + } + current_offset = new_offset; + + zstdio->output.pos = 0; + + if (zstdio->input.pos == 0 && zstdio->output.pos == 0) + break; + } + + if (ret >= 0) + zstdio->saved_offset = file->offset + ret; + + return ret; +} + +/* Release everything, including the underlying file object. */ +static grub_err_t +grub_zstdio_close (grub_file_t file) +{ + zstdio_t zstdio = file->data; + + ZSTD_freeDCtx (zstdio->dctx); + + grub_file_close (zstdio->file); + grub_free (zstdio); + + /* Device must not be closed twice. */ + file->device = 0; + file->name = 0; + return grub_errno; +} + +static struct grub_fs zstdio_fs = { + .name = "zstdio", + .fs_dir = 0, + .fs_open = 0, + .fs_read = grub_zstdio_read, + .fs_close = grub_zstdio_close, + .fs_label = 0, + .next = 0 +}; + +GRUB_MOD_INIT (zstdio) +{ + grub_file_filter_register (GRUB_FILE_FILTER_ZSTDIO, grub_zstdio_open); +} + +GRUB_MOD_FINI (zstdio) +{ + grub_file_filter_unregister (GRUB_FILE_FILTER_ZSTDIO); +} diff --git a/include/grub/file.h b/include/grub/file.h index 16a4b7d26..d51834e6a 100644 --- a/include/grub/file.h +++ b/include/grub/file.h @@ -190,9 +190,10 @@ typedef enum grub_file_filter_id GRUB_FILE_FILTER_GZIO, GRUB_FILE_FILTER_XZIO, GRUB_FILE_FILTER_LZOPIO, + GRUB_FILE_FILTER_ZSTDIO, GRUB_FILE_FILTER_MAX, GRUB_FILE_FILTER_COMPRESSION_FIRST = GRUB_FILE_FILTER_GZIO, - GRUB_FILE_FILTER_COMPRESSION_LAST = GRUB_FILE_FILTER_LZOPIO, + GRUB_FILE_FILTER_COMPRESSION_LAST = GRUB_FILE_FILTER_ZSTDIO, } grub_file_filter_id_t; typedef grub_file_t (*grub_file_filter_t) (grub_file_t in, enum grub_file_type type); From 356dcac9af51344d70dd33b827f8c89f096430dd Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Tue, 21 Oct 2025 09:56:26 -0600 Subject: [PATCH 107/208] tests/file_filter: Regenerate gpg keys The "keys" file is not a valid GPG secret key so it is not possible to generate new signatures. Create a new key and use "gpg --export-secret-key" to export the key and "gpg --export" to export the public key. Then resign all the signatures with "gpg --detach-sign". Signed-off-by: Logan Gunthorpe Reviewed-by: Daniel Kiper --- tests/file_filter/file.gz.sig | Bin 96 -> 310 bytes tests/file_filter/file.lzop.sig | Bin 96 -> 310 bytes tests/file_filter/file.xz.sig | Bin 96 -> 310 bytes tests/file_filter/keys | Bin 994 -> 1308 bytes tests/file_filter/keys.pub | Bin 990 -> 657 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/file_filter/file.gz.sig b/tests/file_filter/file.gz.sig index 602e6187e22d7fc288a52ac572f38cc1066083cb..27cdcff2dac7d94c8a16468431054cc15b2c9cea 100644 GIT binary patch literal 310 zcmV-60m=S}0W$;u0SW*e79j-t9c_qHaZBEqV(=dtEpNLINa*JU0%+3UmH-L~5E?CS zyAMd{=bteM0Eh|rq-p-LiIvH-gwFCsL}^W9@Rh5aTnE_XNOoBFXh7bRxx!`#A-OAZEX4NZ~=M6V3X+7<*;rgVRfyr z8Lm7k0T9UYkIWu11Ac3#6y(_k#56BNOuv?{$9LqA<>a}%mZiQ%&F7=oSOzau(YL6A zFLB;)=bq<$G0#NZo>Mon`-+Ot2p?@nQGn^qk6VBambn-bzmQ@jjqqH(l7LJ4@2aN8 ITqy*<+#TkZDF6Tf literal 96 zcmV-m0H6PeUIYLU2ml5J0#t#n2LK8Q5M~MT@8nv<3h9^t|7f?gmaE1LNh!Sm;mO#P zdLo*tTpkl+s!TB^f%Fh1{Q&^Tlz2`!Q4Giq Ct|zAe diff --git a/tests/file_filter/file.lzop.sig b/tests/file_filter/file.lzop.sig index 7c68dcf931e438973f5c4bfe9a0f9d5ca68fbacc..05949ae7287ad7e102d2ada99284df99de52df8b 100644 GIT binary patch literal 310 zcmV-60m=S}0W$;u0SW*e79j-t9c_qHaZBEqV(=dtEpNLINa*JU0%+3Ur2q;E5E?CS zyAMd{=L9wf|5>JA(bK%c)*r6O*tJpvT^U(2FXqi_npuY=P`-}+{+F1a=QO92UWu=~ zeuC|%dIu1}w&D=S(!LK)sKS>W<^PhzyqF!p_ugD%B>IBVpY~YLkzMWYw%Y+4U)w2S zo`%wlXlQO3_YS+Rl^)f&=>%~9cTa55Z1bDIh@%TIDFX%#t84-Ski|Y9iShWr#C<4j z;g8zJouOM&dBW^7GQ|wOJ7&d(bAmkLlJb%qbv7)%1Dchll zZo>64iqi1{uyFha3{)TW&HPgUCOu;F>jo|~r;|A)iq#{V0`lZmjjNShihg>4#gi8| IC=bA4)%ydMUjP6A literal 96 zcmV-m0H6PeUIYLU2ml5J0#t#n5C94Z5M~MT@8nv<3Q2ST|3TGyOWf``exmR@&j2XX z!ZeX0$UFCBibkPCfL8Tos{sJ3$5~Z-z|*E$2u>jC(Bu@f@NKNp%ec7JvFb-Lz3O)* CVl0pV diff --git a/tests/file_filter/file.xz.sig b/tests/file_filter/file.xz.sig index 57569242e4742cdfb929307ec19440cf91f49e9a..9633a2fa15b2f0d3584afacf84d55a8bd8011fab 100644 GIT binary patch literal 310 zcmV-60m=S}0W$;u0SW*e79j-t9c_qHaZBEqV(=dtEpNLINa*JU0%+3UumB1P5E?CS zyAMd{=XbRS|5<}>|Ee1TE0F9z)>?}P(_Z&yZ7cm#o@dkY_{O6rB<#(vL2LQY_ihb> zUo3YVw(f+y;({{CCGMS&G}n0Vu28A*dC1faO>&3ad-EYTFviW8pBCjl>?XrXwu%?0SExHqzXGq&M-E@jNmALLuc;PkuiAKz$9pl5B*My zErDt-7un4%(mMTB7=i|rVlt{d$@Ep}6n<(4K>QbQ8+R~hbEcEA2LFMcP=XX0hbTlQ zd6SD;7SQD)j28lu@AJklal8J?@IIqDpr6L4mMx=8$UlS)Dx1vZvqeLB9@O|F?Wa?k z+@X3Gxg`64uH-*nxDI-n?A}odx?Lqc`EPUtW*#B@tgjnUqt}hVHW;5M8A+a926XT$M znq5Qz&#wFq?NFhL&UsuPGfxWoCv}^t+>NC%bLk&{_W6T0lZ1X4>R9QTC`0jp61Dvz1-`@a;IsHe=s5s3n;a6;4<0_&Mb9tB^TrNxsVV zF8)1#U%2v%C%ksW&-bDhCb_I^2eE>SSDko~H@Z8ZI$g|WR%05T~ z@bke>UT}`o?U5=FYdAX39K7!sPO5k}-nkfm4fpaAYg>g^53&*Ic*yfIR`7C@s1HEk z7F7fQ*$#`j0u)ZEB5`5w0spB7DW@5m>OY#odJy5W$-N0100-tkDWkj1T;!Ux6(}`& z2k)4NUUc^6YM4GCk!?`N3aoZu(-q;#oF$%Y5d_u*Zl@_CSfJID2F(Gs+94w}b?k|M z95%8WYh9On|Kty$=pk;;4^VmcthBp%>E$IF8w3Eb`~;kP!1Z#bR5Mk9rpAoBav5OV zwzUm3&{>4GfAbhR&hY0}vA#;&xD=ZJ%Qzfu4}dWkMSqo^DCXERpfI?7LgRow`6g=Ck}L4Fz9n2J%B57Hi+vtU``oc{z}nRI5>4x?1*(&a@ZE8L8j@ZIlDe7b~#! zx&+{(f*_j-6|rsM9w5Mj61TXLL8e)<4=MbeyY;s;#|j(U;@(Z)T3bkIi9A{@dOOi43*5N@X-HgTkw~nF`v0d2`U`E^iJ# puwkM8&%e5|8Tg=)pBmQLPAdn2IgOCR{iv zd7*;=eq+{7*`IvWWAkd)@wV4<6dF6}$Q$yhy|Kltkvt90iqlY|7JP5nf#rM#2DtCu zh;9+E;AJ;0rBpA9l>pwU9}N;t?D?~oCe9{=*0AjC?Qr>j;bOq!5$Yuocx8582a-E4 zdcJPf3L`Si&UU?(3z3{?RFoaWXS7$|2nd{_-eJlv%`a=7@EDk#fE)EI@YVq?Xao9I zbMOvW0x7t$Dprmb0WH@nYr%3ekt!nn7e-d1%82Z;yj>wC039nL$s34N?EW!-Av$so z!`DA-$eIU#*?$H0M~=H>WfW$`PXPe<{-J%q5gI}NA3-`^az(7r5xTAwL#+mBz|^?@ zI}fu5{wxKxDZP{FUck6;a(sE4bpk=Wc0wH$W)=p%kG+MS!RmG~OWi4<;3=Oo{5BqH zBg~!VR|NBhf=d&J|B$j25JzIBp9Kld$l#yDvuz=NdN^nA@=cwz&`Ezpv7R;6gk|My z_?I8#%?Z*v0G=T3S|-k)ND-11n=+U)J=DUmZ`*NlWO1qRl1d|e3jZ@ioyPN?bxzIt zo0ltfBGhMj*HF^AUdvgzSm>LzM75fVG>0caAHZcq9dkq@E1ccwLg7IkAD-Uj9eRWe zy-8PpA1OO@l`ln1(1=Rp8vO zhuG2w{xno40uuPIPG?MWCC0Q2-{V6FS{Cz}EfnlhM@>u=!-(=uFastW7Q++Frx~}7 zMgL46c$uH4S-c-UuJEc>o1Z`}^Dg{%_Ev0vu#BwZ0~=9rNw3S14`YP`A(m%EqsL=| zcV{&Z6f~)GHeZAo+IDlC?FaI?=82AmOaOiEXonNmLLN zhNI5T)a!L9eJdZ0gpLW2y;mIa;GlWR;;#iv$ja>xBSV4sVvx=}>7h*2=CQ4Ak3YL#Z1|T%6Q`uC%j;uNS@9jNmALLuc;PkuiAKz$9pl5B*My zErDt-7un4%(mMTB7=i|rVlt{d$@Ep}6n<(4K>QbQ8+R~hbEcEA2LFMcP=XX0hbTlQ zd6SD;7SQD)j28lu@AJklal8J?@IIqDpr6L4mMx=8$UlS)Dx1vZvqeLB9@O|F?Wa?k z+@X3Gxg`4%uH-*nxDI-n?A`6g=Ck}L4Fz9n2J%B57Hi+vtU``oc{z}nRI5>4 zx?1*(&a@ZE8L8j@ZIlDe7b~#!x&+{(f*_j-6|rsM9w5Mj61TXLL8e)<4=MbeyY;s; z#|j(U;@(Z)T3bkIi9A{@dOOi43*5 zN@X-HgTkw~nF`v0d2`U`E^iJ#uwkM8&%e5|8Tg=)pBmQLPAdn2IgOCR{ivp9m4OgLnFB5aRDrEf5eNXak$CR!^i^qM-2=)M(8-`vDQdcaw{#q%DO)*> zd7*;=eq+{7*`IvWWAkd)@wV4<6dF6}$Q$yhy|Kltkvt90iqlY|7JP5nf#rM#2DtCu zh;9+E;AJ;0rBpA9l>pwU9}N;t?D?~oCe9{=*0AjC?Qr>j;bOq!5$Yuocx8582a-E4 zdcJPf3L`Si&UU?(3z3{?RFoaWXS7$|2nd{_-eJlv%`a=7@EDk#fE)EI@YVq?Xao9I zbMOvW0x7t$Dprmb0WH@nYr%3ekt!nn7e-d1%82Z;yj>wC039nL$s34N?EW!-Av$so z!`DA-$eIU#*?$H0M~=H>WfW$`PXPe<{-J%q5gI}NA3-`^az(7r5xTAwL#+mBz|^?@ zI}fu5{wxKxDZP{FUck6;a(sE4bpk=Wc0wH$W)=p%kG+MS!RmG~OWi4<;3=Oo{5BqH zBg~!VR|NBhf=d&J|B$j25JzIBp9Kld$l#yDvuz=NdN^nA@=cwz&`Ezpv7R;6gk|My z_?I8#%?Z*v0G=T3S|-k)ND-11n=+U)J=DUmZ`*NlWO1qRl1d|e3jZ@ioyPN?bxzIt zo0ltfBGhMj*HF^AUdvgzSm>LzM75fVG>0caAHZcq9dkq@E1ccwLg7IkAD-Uj9eRWe zy-8PpA1OO@l`ln1(1=Rp8vO zhuG2w{xno40uuPIPG?MWCC0Q2-{V6FS{Cz}EfnlhM@>u=!-(=uFastW7Q++Frx~}7 zMgL46c$uH4S-c-UuJEc>o1Z`}^Dg{%_Ev0vu#BwZ0~=9rNw3S14`YP`A(m%EqsL=| zcV{&Z6f~)GHeZAo+IDlC?FaI?=82AmOaOiEXonNmLLN zhNI5T)a!L9eJdZ0gpLW2y;mIa;GlWR;;#iv$ja>xBSV4sVvx=}>7h*2=CQ4Ak3YL#Z1|T%6Q`uC%j;uNS@9 Date: Tue, 21 Oct 2025 09:56:27 -0600 Subject: [PATCH 108/208] tests/file_filter: Add zstd test file Add a file.zstd similar to the other compression methods and generate a gpg signature with "gpg --detach-sign". Signed-off-by: Logan Gunthorpe Reviewed-by: Daniel Kiper --- tests/file_filter/file.zstd | Bin 0 -> 26 bytes tests/file_filter/file.zstd.sig | Bin 0 -> 310 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/file_filter/file.zstd create mode 100644 tests/file_filter/file.zstd.sig diff --git a/tests/file_filter/file.zstd b/tests/file_filter/file.zstd new file mode 100644 index 0000000000000000000000000000000000000000..6ea24ccf8390f456a63f15e1d0f473d0a0ebc75e GIT binary patch literal 26 hcmdPcs{fZIB9no^BQ+-{Uq_*|IJHQTYg5;2b^v!n2`2ym literal 0 HcmV?d00001 diff --git a/tests/file_filter/file.zstd.sig b/tests/file_filter/file.zstd.sig new file mode 100644 index 0000000000000000000000000000000000000000..ab101bb6e883a09dd41ab8fe348cf8a9ce5843c7 GIT binary patch literal 310 zcmV-60m=S}0W$;u0SW*e79j-t9c_qHaZBEqV(=dtEpNLINa*JU0%+3Wr2q;E5E?CS zyAMd{=f%Sa0D;xR-EXs|$ZnV8EKm8`Acg4~^9(E4y~^NzaINTQYg%`Gg2<@;Fpno=K?r5u)5TM|R$F@ovn27&G8fg-pVRvhu}ovsacO<}u{ z25i6uvgdqAkCTR$d4F3pccusj##rB9AXS&mdn)t-MYSd?m-HI2<^5Isfdl;1i)xUX zxKdFt^#|YWXW1hao$90KM#Cx?ic2Sv^Si30&h0m4<=XFvh;2}vaiGu|h={rI3w*pU zi7KP2UVUACi2ubxOq2^og4khH%5%bHKw9Mcwh)Sob&Oz#ur6US1>g2>8 Date: Tue, 21 Oct 2025 09:56:28 -0600 Subject: [PATCH 109/208] tests/file_filter: Add zstd tests Test zstd decompression in the same way that other decompressors are tested. Signed-off-by: Logan Gunthorpe Reviewed-by: Daniel Kiper --- tests/file_filter/test.cfg | 1 + tests/file_filter_test.in | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/file_filter/test.cfg b/tests/file_filter/test.cfg index 4308aaca5..ec4d10ab4 100644 --- a/tests/file_filter/test.cfg +++ b/tests/file_filter/test.cfg @@ -3,4 +3,5 @@ set check_signatures=enforce cat /file.gz cat /file.xz cat /file.lzop +cat /file.zstd set check_signatures= diff --git a/tests/file_filter_test.in b/tests/file_filter_test.in index ed6abcb5a..18324f1a0 100644 --- a/tests/file_filter_test.in +++ b/tests/file_filter_test.in @@ -19,14 +19,15 @@ grubshell=@builddir@/grub-shell . "@builddir@/grub-core/modinfo.sh" -filters="gzio xzio lzopio pgp" +filters="gzio xzio lzopio zstdio pgp" modules="cat mpi" for mod in $(cut -d ' ' -f 2 "@builddir@/grub-core/crypto.lst" | sort -u); do modules="$modules $mod" done -for file in file.gz file.xz file.lzop file.gz.sig file.xz.sig file.lzop.sig keys.pub; do +for file in file.gz file.xz file.lzop file.zstd file.gz.sig file.xz.sig \ + file.lzop.sig file.zstd.sig keys.pub; do files="$files /$file=@srcdir@/tests/file_filter/$file" done @@ -35,6 +36,8 @@ result="Hello, user! Hello, user! +Hello, user! + Hello, user!" out="$("${grubshell}" --modules="$modules $filters" --files="$files" "@srcdir@/tests/file_filter/test.cfg")" From 42c0997864a8d13f8a6b6eddf14087af5134ef2f Mon Sep 17 00:00:00 2001 From: Leo Sandoval Date: Tue, 21 Oct 2025 12:39:00 -0600 Subject: [PATCH 110/208] commands/memtools: Add lsmemregions command Prints memory regions general information including size, number of blocks, total free and total allocated memory per region. The reason behind is to have a tool that shows general information about regions and how fragmented the memory is at some particular time. Below is an example showing how this tool before and after memory stress. grub> lsmemregions Region 0x78f6e000 (size 33554368 blocks 1048574 free 27325472 alloc 6232768) > stress_big_allocations ... grub> lsmemregions Region 0x7af8e000 (size 4032 blocks 126 free 2720 alloc 1312) Region 0x80c000 (size 81856 blocks 2558 free 81856 alloc 0) Region 0x7d165000 (size 167872 blocks 5246 free 167872 alloc 0) Region 0x7d0bf000 (size 655296 blocks 20478 free 655296 alloc 0) Region 0x7ee00000 (size 1331136 blocks 41598 free 1331136 alloc 0) Region 0x100000 (size 7385024 blocks 230782 free 7385024 alloc 0) Region 0x7af95000 (size 25382848 blocks 793214 free 25382848 alloc 0) Region 0x1780000 (size 2038357952 blocks 63698686 free 2077517536 alloc 5445568) Signed-off-by: Leo Sandoval Reviewed-by: Andrew Hamilton Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/commands/memtools.c | 17 ++++++++++++++++- grub-core/kern/mm.c | 33 +++++++++++++++++++++++++++++++++ include/grub/mm.h | 1 + 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/grub-core/commands/memtools.c b/grub-core/commands/memtools.c index ae0a9bec3..f9ba45efa 100644 --- a/grub-core/commands/memtools.c +++ b/grub-core/commands/memtools.c @@ -53,6 +53,18 @@ grub_cmd_lsfreemem (grub_command_t cmd __attribute__ ((unused)), return 0; } +static grub_err_t +grub_cmd_lsmemregions (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) + +{ +#ifndef GRUB_MACHINE_EMU + grub_mm_dump_regions (); +#endif + + return 0; +} static grub_err_t grub_cmd_stress_big_allocs (grub_command_t cmd __attribute__ ((unused)), @@ -132,7 +144,7 @@ grub_cmd_stress_big_allocs (grub_command_t cmd __attribute__ ((unused)), return GRUB_ERR_NONE; } -static grub_command_t cmd_lsmem, cmd_lsfreemem, cmd_sba; +static grub_command_t cmd_lsmem, cmd_lsfreemem, cmd_lsmemregions, cmd_sba; GRUB_MOD_INIT (memtools) { @@ -140,6 +152,8 @@ GRUB_MOD_INIT (memtools) 0, N_("List free and allocated memory blocks.")); cmd_lsfreemem = grub_register_command ("lsfreemem", grub_cmd_lsfreemem, 0, N_("List free memory blocks.")); + cmd_lsmemregions = grub_register_command ("lsmemregions", grub_cmd_lsmemregions, + 0, N_("List memory regions.")); cmd_sba = grub_register_command ("stress_big_allocs", grub_cmd_stress_big_allocs, 0, N_("Stress test large allocations.")); } @@ -148,5 +162,6 @@ GRUB_MOD_FINI (memtools) { grub_unregister_command (cmd_lsmem); grub_unregister_command (cmd_lsfreemem); + grub_unregister_command (cmd_lsmemregions); grub_unregister_command (cmd_sba); } diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c index 027a25cd1..5f769a299 100644 --- a/grub-core/kern/mm.c +++ b/grub-core/kern/mm.c @@ -786,6 +786,39 @@ grub_mm_dump (unsigned lineno) grub_printf ("\n"); } +void +grub_mm_dump_regions (void) +{ + grub_mm_region_t r; + grub_mm_header_t p; + grub_size_t num_blocks, sum_free, sum_alloc; + + for (r = grub_mm_base; r; r = r->next) + { + num_blocks = 0; + sum_free = 0; + sum_alloc = 0; + + p = (grub_mm_header_t) ALIGN_UP ((grub_addr_t) (r + 1), GRUB_MM_ALIGN); + for (; (grub_addr_t) p < (grub_addr_t) (r+1) + r->size; p++, num_blocks++) + { + switch (p->magic) + { + case GRUB_MM_FREE_MAGIC: + sum_free += p->size; + break; + case GRUB_MM_ALLOC_MAGIC: + sum_alloc += p->size; + break; + } + } + grub_printf ("Region %p (size %" PRIuGRUB_SIZE " blocks %" PRIuGRUB_SIZE " free %" PRIuGRUB_SIZE " alloc %" PRIuGRUB_SIZE ")\n\n", + r, r->size, num_blocks, sum_free << GRUB_MM_ALIGN_LOG2, sum_alloc << GRUB_MM_ALIGN_LOG2); + } + + grub_printf ("\n"); +} + void * grub_debug_calloc (const char *file, int line, grub_size_t nmemb, grub_size_t size) { diff --git a/include/grub/mm.h b/include/grub/mm.h index 51ec0b8f9..06956484c 100644 --- a/include/grub/mm.h +++ b/include/grub/mm.h @@ -100,6 +100,7 @@ extern int EXPORT_VAR(grub_mm_debug); void EXPORT_FUNC(grub_mm_dump_free) (void); void EXPORT_FUNC(grub_mm_dump) (unsigned lineno); +void EXPORT_FUNC(grub_mm_dump_regions) (void); #define grub_calloc(nmemb, size) \ grub_debug_calloc (GRUB_FILE, __LINE__, nmemb, size) From fca6c0afdaec07a2bad91894b1deab63ad3211a9 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sun, 19 Oct 2025 08:46:20 -0500 Subject: [PATCH 111/208] docs: Document lsmemregions and memtools commands Add documentation of the new lsmemregions command as well as documenting the existing memtools module commands. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- docs/grub.texi | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/docs/grub.texi b/docs/grub.texi index 642dd4c67..5b23ae47b 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -5352,7 +5352,8 @@ enforced (@pxref{Lockdown}). @node memtools_module @section memtools This module provides support for GRUB development / debugging commands -@command{lsmem}, @command{lsfreemem}, and @command{stress_big_allocs}. +@command{lsmem}, @command{lsfreemem}, @command{lsmemregions}, and +@command{stress_big_allocs}. @node minicmd_module @section minicmd @@ -6697,7 +6698,10 @@ you forget a command, you can run the command @command{help} * loopback:: Make a device from a filesystem image * ls:: List devices or files * lsfonts:: List loaded fonts +* lsfreemem:: List free memory blocks * lsmod:: Show loaded modules +* lsmem:: List free and allocated memory blocks +* lsmemregions:: List memory regions * md5sum:: Compute or check MD5 hash * module:: Load module for multiboot kernel * multiboot:: Load multiboot compliant kernel @@ -6725,6 +6729,7 @@ you forget a command, you can run the command @command{help} * sleep:: Wait for a specified number of seconds * smbios:: Retrieve SMBIOS information * source:: Read a configuration file in same context +* stress_big_allocs:: Stress test large memory allocations * test:: Check file types and compare values * tpm2_key_protector_init:: Initialize the TPM2 key protector * tpm2_key_protector_clear:: Clear the TPM2 key protector @@ -7810,6 +7815,14 @@ List loaded fonts. @end deffn +@node lsfreemem +@subsection lsfreemem + +@deffn Command lsfreemem +List free memory blocks. +@end deffn + + @node lsmod @subsection lsmod @@ -7817,6 +7830,24 @@ List loaded fonts. Show list of loaded modules. @end deffn + +@node lsmem +@subsection lsmem + +@deffn Command lsmem +List free and allocated memory blocks. +@end deffn + + +@node lsmemregions +@subsection lsmemregions + +@deffn Command lsmemregions +Prints memory region general information including size, number of +blocks, and total free / total allocated memory per region. +@end deffn + + @node md5sum @subsection md5sum @@ -8458,6 +8489,14 @@ will not be shown immediately. @end deffn +@node stress_big_allocs +@subsection stress_big_allocs + +@deffn Command stress_big_allocs +Stress test large memory allocations. +@end deffn + + @node test @subsection test From cd24e259108aa433115345952f5b7ed918de0afa Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Tue, 21 Oct 2025 21:20:03 +0000 Subject: [PATCH 112/208] net/bootp: Prevent a UAF in network interface unregister A UAF occurs in grub_net_network_level_interface_unregister() when inter->name is accessed after being freed in grub_cmd_bootp(). Fix it by deferring grub_free(ifaces[j].name) until after grub_net_network_level_interface_unregister() completes. Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/net/bootp.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index 2f45a3cc2..fa3834f63 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -901,14 +901,17 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), err = GRUB_ERR_NONE; for (j = 0; j < ncards; j++) { - grub_free (ifaces[j].name); if (!ifaces[j].prev) - continue; + { + grub_free (ifaces[j].name); + continue; + } grub_error_push (); grub_net_network_level_interface_unregister (&ifaces[j]); err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("couldn't autoconfigure %s"), ifaces[j].card->name); + grub_free (ifaces[j].name); } grub_free (ifaces); From fadc94b919a2d35c3f81c3593004af14b4535701 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Tue, 21 Oct 2025 21:20:04 +0000 Subject: [PATCH 113/208] net/dns: Prevent UAF and double free In recv_hook(), *data->addresses is freed without being set to NULL. Since *data->addresses can be cached in dns_cache[h].addresses, this can lead to UAF or double free if dns_cache[h].addresses is accessed or cleared later. The fix sets *data->addresses to NULL after freeing to avoid dangling pointer. Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/net/dns.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c index f20cd6f83..bef697d98 100644 --- a/grub-core/net/dns.c +++ b/grub-core/net/dns.c @@ -424,7 +424,10 @@ recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)), grub_netbuff_free (nb); grub_free (redirect_save); if (!*data->naddresses) - grub_free (*data->addresses); + { + grub_free (*data->addresses); + *data->addresses = NULL; + } return GRUB_ERR_NONE; } From c2cae77abe4ffad2ec84e7f82ced123739429cf8 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Fri, 17 Oct 2025 18:35:59 +0000 Subject: [PATCH 114/208] net/tftp: Fix NULL pointer dereference in grub_net_udp_close() A NULL pointer dereference can occur in grub_net_udp_close(data->sock) when handling a malformed TFTP OACK packet. This issue was discovered via fuzzing. When a malformed OACK packet contains an invalid file size, "tsize", value tftp_receive() detects the error and saves it via grub_error_save(&data->save_err). Later, tftp_open() restores this error and calls grub_net_udp_close(data->sock) assuming the socket is still valid. However, the socket may have already been closed and set to NULL after processing the final data block in tftp_receive() leading to a NULL pointer dereference when attempting to close it again. Fix it by checking if the socket is non-NULL before closing. Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper Reviewed-by: Sudhakar Kuppusamy --- grub-core/net/tftp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index 336b78691..63953bc19 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -412,7 +412,11 @@ tftp_open (struct grub_file *file, const char *filename) grub_error_load (&data->save_err); if (grub_errno) { - grub_net_udp_close (data->sock); + if (data->sock != NULL) + { + grub_net_udp_close (data->sock); + data->sock = NULL; + } grub_free (data); file->data = NULL; return grub_errno; From 5ff9c43cfe01964ac0b7eb0481a8a70adbdd98d6 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Mon, 13 Oct 2025 14:52:24 +0800 Subject: [PATCH 115/208] kern/ieee1275/init: Use net config for boot location instead of firmware bootpath On network boots grub_ieee1275_net_config() is used to determine the boot device but the path continues to be taken from the Open Firmware /chosen/bootpath property. This assumes the device node follows the generic IEEE 1275 syntax which is not always the case. Different drivers may extend or redefine the format and GRUB may then misinterpret the argument as a filename and set $prefix incorrectly. The generic Open Firmware device path format is: device-name[:device-argument] device-argument := [partition][,[filename]] For example, a bootpath such as: /vdevice/l-lan@30000002:speed=auto,duplex=auto,1.2.243.345,,9.8.76.543,1.2.34.5,5,5,255.255.255.0,512 does not follow this form. The section after the colon (the device-argument) contains driver-specific options and network parameters, not a valid filename. The GRUB interprets this string as a filename which results in $prefix being set to "/", effectively losing the intended boot directory. The firmware is not at fault here since interpretation of device nodes is driver-specific. Instead, GRUB should use the filename provided in the cached DHCP packet which is consistent and reliable. This is also the same mechanism already used on UEFI and legacy BIOS platforms. This patch updates grub_machine_get_bootlocation() to prefer the result from grub_ieee1275_net_config() when complete and only fall back to the firmware bootpath otherwise. Signed-off-by: Michael Chang Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/kern/ieee1275/init.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 0ef888058..7cdcd750f 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -162,6 +162,8 @@ grub_machine_get_bootlocation (char **device, char **path) char *bootpath; char *filename; char *type; + char *ret_device = NULL; + char *ret_path = NULL; bootpath = grub_ieee1275_get_boot_dev (); if (! bootpath) @@ -177,7 +179,7 @@ grub_machine_get_bootlocation (char **device, char **path) dev = grub_ieee1275_get_aliasdevname (bootpath); canon = grub_ieee1275_canonicalise_devname (dev); if (! canon) - return; + goto done; ptr = canon + grub_strlen (canon) - 1; while (ptr > canon && (*ptr == ',' || *ptr == ':')) ptr--; @@ -185,13 +187,17 @@ grub_machine_get_bootlocation (char **device, char **path) *ptr = 0; if (grub_ieee1275_net_config) - grub_ieee1275_net_config (canon, device, path, bootpath); + grub_ieee1275_net_config (canon, &ret_device, &ret_path, bootpath); grub_free (dev); grub_free (canon); + + /* Use path from net config if it is provided by cached DHCP info */ + if (ret_path != NULL) + goto done; + /* Fall through to use firmware bootpath */ } else - *device = grub_ieee1275_encode_devname (bootpath); - grub_free (type); + ret_device = grub_ieee1275_encode_devname (bootpath); filename = grub_ieee1275_get_filename (bootpath); if (filename) @@ -204,10 +210,18 @@ grub_machine_get_bootlocation (char **device, char **path) *lastslash = '\0'; grub_translate_ieee1275_path (filename); - *path = filename; + ret_path = filename; } } + + done: + grub_free (type); grub_free (bootpath); + + if (device != NULL) + *device = ret_device; + if (path != NULL) + *path = ret_path; } /* Claim some available memory in the first /memory node. */ From 000e48b42c7e41d0f75cb7c6d5bcc76f8df6311f Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sun, 1 Jun 2025 10:52:22 -0500 Subject: [PATCH 116/208] fs/ntfs: Correct next_attribute validation Improved ad-hoc fuzzing coverage revealed a possible access violation around line 342 of grub-core/fs/ntfs.c when accessing the attr_cur pointer due to possibility of moving pointer "next" beyond of the end of the valid buffer inside next_attribute. Prevent this for cases where full attribute validation is not performed (such as on attribute lists) by performing a sanity check on the newly calculated next pointer. Fixes: 06914b614 (fs/ntfs: Correct attribute vs attribute list validation) Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index 5b0a18f3d..bb3cec4e6 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -233,7 +233,12 @@ next_attribute (grub_uint8_t *curr_attribute, void *end, bool validate) return NULL; next += u16at (curr_attribute, 4); - if (validate && validate_attribute (next, end) == false) + if (validate) + { + if (validate_attribute (next, end) == false) + return NULL; + } + else if (next >= (grub_uint8_t *) end) return NULL; return next; From 4129e9ad6feef6c609214ee30d6839740629039d Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Thu, 23 Oct 2025 14:02:50 +0200 Subject: [PATCH 117/208] tss2: Always init out buffer before calling tpm2_submit_command_real() When tpm2_submit_command_real() is called for a retry, the content of out buffer can already be set with previous tpm2_submit_command_real() call's reply. Add a call to grub_tpm2_buffer_init() before tpm2_submit_command_real(). This solves the issues occurring during TPM_CC_Load command on the integrated TPM 2.0 in Intel Elkhart Lake chip. Signed-off-by: Yann Diorcet Reviewed-by: Daniel Kiper --- grub-core/lib/tss2/tpm2_cmd.c | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/grub-core/lib/tss2/tpm2_cmd.c b/grub-core/lib/tss2/tpm2_cmd.c index b682eb431..c6996cac8 100644 --- a/grub-core/lib/tss2/tpm2_cmd.c +++ b/grub-core/lib/tss2/tpm2_cmd.c @@ -89,6 +89,7 @@ tpm2_submit_command (const TPMI_ST_COMMAND_TAG_t tag, /* Catch TPM_RC_RETRY and send the command again */ do { + grub_tpm2_buffer_init (out); err = tpm2_submit_command_real (tag, commandCode, responseCode, in, out); if (*responseCode != TPM_RC_RETRY) break; @@ -167,7 +168,6 @@ grub_tpm2_createprimary (const TPMI_RH_HIERARCHY_t primaryHandle, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_CreatePrimary, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -250,7 +250,6 @@ grub_tpm2_startauthsession (const TPMI_DH_OBJECT_t tpmKey, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_StartAuthSession, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) @@ -308,7 +307,6 @@ grub_tpm2_policypcr (const TPMI_SH_POLICY_t policySessions, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_PolicyPCR, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -347,7 +345,6 @@ grub_tpm2_readpublic (const TPMI_DH_OBJECT_t objectHandle, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_ReadPublic, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -408,7 +405,6 @@ grub_tpm2_load (const TPMI_DH_OBJECT_t parent_handle, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_Load, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -475,7 +471,6 @@ grub_tpm2_loadexternal (const TPMS_AUTH_COMMAND_t *authCommand, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_LoadExternal, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -527,7 +522,6 @@ grub_tpm2_unseal (const TPMI_DH_OBJECT_t itemHandle, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_Unseal, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -561,7 +555,6 @@ grub_tpm2_flushcontext (const TPMI_DH_CONTEXT_t handle) return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (TPM_ST_NO_SESSIONS, TPM_CC_FlushContext, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -609,7 +602,6 @@ grub_tpm2_pcr_event (const TPMI_DH_PCR_t pcrHandle, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (TPM_ST_SESSIONS, TPM_CC_PCR_Event, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -666,7 +658,6 @@ grub_tpm2_pcr_read (const TPMS_AUTH_COMMAND_t *authCommand, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_PCR_Read, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -719,7 +710,6 @@ grub_tpm2_policygetdigest (const TPMI_SH_POLICY_t policySession, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_PolicyGetDigest, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -802,7 +792,6 @@ grub_tpm2_create (const TPMI_DH_OBJECT_t parentHandle, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_Create, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -856,7 +845,6 @@ grub_tpm2_evictcontrol (const TPMI_RH_PROVISION_t auth, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_EvictControl, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -922,7 +910,6 @@ grub_tpm2_hash (const TPMS_AUTH_COMMAND_t *authCommand, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_Hash, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -981,7 +968,6 @@ grub_tpm2_verifysignature (const TPMI_DH_OBJECT_t keyHandle, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_VerifySignature, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -1041,7 +1027,6 @@ grub_tpm2_policyauthorize (const TPMI_SH_POLICY_t policySession, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_PolicyAuthorize, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -1082,7 +1067,6 @@ grub_tpm2_testparms (const TPMT_PUBLIC_PARMS_t *parms, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_TestParms, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) @@ -1126,7 +1110,6 @@ grub_tpm2_nv_definespace (const TPMI_RH_PROVISION_t authHandle, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_NV_DefineSpace, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -1161,7 +1144,6 @@ grub_tpm2_nv_undefinespace (const TPMI_RH_PROVISION_t authHandle, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_NV_UndefineSpace, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -1197,7 +1179,6 @@ grub_tpm2_nv_readpublic (const TPMI_RH_NV_INDEX_t nvIndex, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_NV_ReadPublic, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -1242,7 +1223,6 @@ grub_tpm2_nv_read (const TPMI_RH_NV_AUTH_t authHandle, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_NV_Read, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; @@ -1284,7 +1264,6 @@ grub_tpm2_nv_write (const TPMI_RH_NV_AUTH_t authHandle, return TPM_RC_FAILURE; /* Submit */ - grub_tpm2_buffer_init (&out); rc = tpm2_submit_command (tag, TPM_CC_NV_Write, &responseCode, &in, &out); if (rc != TPM_RC_SUCCESS) return rc; From 7ded35feaab4fe4609606ead4da932c4164b9cb0 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Thu, 23 Oct 2025 19:18:17 +0200 Subject: [PATCH 118/208] bootstrap: Fix patching warnings Currently bootstrap complains in the following way when patching gnulib files: patching file regcomp.c Hunk #2 succeeded at 1029 with fuzz 2. Hunk #5 succeeded at 1716 with fuzz 2. patching file regexec.c patching file base64.c patching file regexec.c Hunk #1 succeeded at 807 (offset -21 lines). Let's fix it by adding missing "\f" and amending line numbers in the patches. Signed-off-by: Daniel Kiper Reviewed-by: Alec Brown --- grub-core/lib/gnulib-patches/fix-regcomp-resource-leak.patch | 4 ++-- grub-core/lib/gnulib-patches/fix-unused-value.patch | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/grub-core/lib/gnulib-patches/fix-regcomp-resource-leak.patch b/grub-core/lib/gnulib-patches/fix-regcomp-resource-leak.patch index b2e900023..62e7623a6 100644 --- a/grub-core/lib/gnulib-patches/fix-regcomp-resource-leak.patch +++ b/grub-core/lib/gnulib-patches/fix-regcomp-resource-leak.patch @@ -50,7 +50,7 @@ - return REG_NOERROR; + return err; } - + /* If it is possible to do searching in single byte encoding instead of UTF-8 @@ -1677,12 +1677,11 @@ calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, Idx node, bool root) { @@ -106,5 +106,5 @@ + + return err; } - + /* Functions for token which are used in the parser. */ diff --git a/grub-core/lib/gnulib-patches/fix-unused-value.patch b/grub-core/lib/gnulib-patches/fix-unused-value.patch index ba51f1bf2..b56ab4bb4 100644 --- a/grub-core/lib/gnulib-patches/fix-unused-value.patch +++ b/grub-core/lib/gnulib-patches/fix-unused-value.patch @@ -1,6 +1,6 @@ --- a/lib/regexec.c 2020-10-21 14:25:35.310195912 +0000 +++ b/lib/regexec.c 2020-10-21 14:32:07.961765604 +0000 -@@ -828,7 +828,11 @@ +@@ -807,7 +807,11 @@ break; if (__glibc_unlikely (err != REG_NOMATCH)) goto free_return; From 11845da2b1ffebf3e9b67dd21ae8c743746d5854 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Thu, 23 Oct 2025 19:37:49 +0200 Subject: [PATCH 119/208] lib/xzembed/xz_dec_stream: Replace grub_memcpy() call with memcpy() Make the code consistent. Signed-off-by: Daniel Kiper Reviewed-by: Alec Brown --- grub-core/lib/xzembed/xz_dec_stream.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/grub-core/lib/xzembed/xz_dec_stream.c b/grub-core/lib/xzembed/xz_dec_stream.c index f2dd42dc2..13658d448 100644 --- a/grub-core/lib/xzembed/xz_dec_stream.c +++ b/grub-core/lib/xzembed/xz_dec_stream.c @@ -387,8 +387,7 @@ static enum xz_ret hash_validate(struct xz_dec *s, struct xz_buf *b, && sizeof (s->hash_value) >= hash->mdlen) { hash->final(hash_context); - grub_memcpy (s->hash_value, hash->read(hash_context), - hash->mdlen); + memcpy (s->hash_value, hash->read (hash_context), hash->mdlen); s->have_hash_value = 1; if (s->hash_id == 1 || crc32) { From 8271bcc13087c2437b74d0ed181c6e8463526c3b Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Fri, 24 Oct 2025 20:11:14 +0200 Subject: [PATCH 120/208] build: Add appended signatures header file to EXTRA_DIST This file was not added to EXTRA_DIST during the appended signatures merge. Fixes: 3e4ff6ffb (appended signatures: Parse ASN1 node) Signed-off-by: Daniel Kiper Reviewed-by: Alec Brown --- conf/Makefile.extra-dist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index 846d45341..dcb0bed07 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -76,6 +76,8 @@ EXTRA_DIST += grub-core/tests/asn1/asn1_test.h EXTRA_DIST += $(shell find $(top_srcdir)/grub-core/tests/asn1/tests -name '*.h') EXTRA_DIST += $(shell find $(top_srcdir)/grub-core/commands/tpm2_key_protector -name '*.h') +EXTRA_DIST += grub-core/commands/appendedsig/appendedsig.h + EXTRA_DIST += grub-core/lib/LzmaDec.c EXTRA_DIST += grub-core/fs/cpio_common.c From fa79d5ea9c5c65467531aba8af931f1cb637b354 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sat, 25 Oct 2025 10:53:37 -0500 Subject: [PATCH 121/208] build: Include MAINTAINERS and SECURITY files in dist archive Include the MAINTAINERS and SECURITY files in the dist archive for reference in distributed archives by end users. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- conf/Makefile.extra-dist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index dcb0bed07..936d3bef6 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -83,6 +83,8 @@ EXTRA_DIST += grub-core/lib/LzmaDec.c EXTRA_DIST += grub-core/fs/cpio_common.c EXTRA_DIST += BUGS +EXTRA_DIST += MAINTAINERS +EXTRA_DIST += SECURITY EXTRA_DIST += util/i386/efi/grub-dumpdevtree EXTRA_DIST += util/spkmodem-recv.c EXTRA_DIST += util/import_gcrypth.sed From 46be4488fd24a194315ed59e8a4c34150db3727f Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sat, 25 Oct 2025 10:53:38 -0500 Subject: [PATCH 122/208] build: Include new zstd test support files in dist archive Include the two new zstd test support files in the dist archive so end users can successfully run this test. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- conf/Makefile.extra-dist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index 936d3bef6..6b276b187 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -183,6 +183,8 @@ EXTRA_DIST += tests/file_filter/file.lzop EXTRA_DIST += tests/file_filter/file.lzop.sig EXTRA_DIST += tests/file_filter/file.xz EXTRA_DIST += tests/file_filter/file.xz.sig +EXTRA_DIST += tests/file_filter/file.zstd +EXTRA_DIST += tests/file_filter/file.zstd.sig EXTRA_DIST += tests/file_filter/keys EXTRA_DIST += tests/file_filter/keys.pub EXTRA_DIST += tests/file_filter/test.cfg From f62269767ed9784939f880bfa8179e87d521ca49 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sat, 25 Oct 2025 10:53:39 -0500 Subject: [PATCH 123/208] build: Add tpm2key.asn file for reference to dist archive Add the tpm2key.asn file to the dist archive for reference by end users. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- conf/Makefile.extra-dist | 1 + 1 file changed, 1 insertion(+) diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index 6b276b187..288ba1f87 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -75,6 +75,7 @@ EXTRA_DIST += grub-core/efiemu/runtime/config.h EXTRA_DIST += grub-core/tests/asn1/asn1_test.h EXTRA_DIST += $(shell find $(top_srcdir)/grub-core/tests/asn1/tests -name '*.h') EXTRA_DIST += $(shell find $(top_srcdir)/grub-core/commands/tpm2_key_protector -name '*.h') +EXTRA_DIST += grub-core/commands/tpm2_key_protector/tpm2key.asn EXTRA_DIST += grub-core/commands/appendedsig/appendedsig.h From c602035a9f078df532df8da69e5b43652a9ad600 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sat, 25 Oct 2025 10:53:40 -0500 Subject: [PATCH 124/208] loader/efi/linux: Fix compile error with Clang Clang will produce a warning, which is treated as an error, that "vendor_defined_data" is uninitialized. This is a "zero length" array member of this struct. Add conditional compile pragma to allow this to compile with Clang. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- grub-core/loader/efi/linux.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c index 4f5589862..ded507cd5 100644 --- a/grub-core/loader/efi/linux.c +++ b/grub-core/loader/efi/linux.c @@ -55,6 +55,14 @@ static bool initrd_use_loadfile2 = false; static grub_guid_t load_file2_guid = GRUB_EFI_LOAD_FILE2_PROTOCOL_GUID; static grub_guid_t device_path_guid = GRUB_EFI_DEVICE_PATH_GUID; +/* + * Clang will produce a warning for missing initializer for the + * zero-length array "vendor_defined_data" inside this structure. + * Suppress this warning which is treated as an error. + */ +#ifdef __clang__ +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif static initrd_media_device_path_t initrd_lf2_device_path = { { { From a340750f9e2e234be37df34f422eb5bfd799e75f Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Mon, 27 Oct 2025 16:56:39 +0100 Subject: [PATCH 125/208] windows: Fix relocation sections generation during module conversion from PE to ELF The commit 98ad84328 (kern/dl: Check for the SHF_INFO_LINK flag in grub_dl_relocate_symbols()) revealed a bug in the code converting PE module images to ELF files. The missing SHF_INFO_LINK flag for SHT_REL and SHT_RELA sections lead to hangs during GRUB load. This only happens for the GRUB images generated on Windows platforms. The *NIX platforms are not affected due to lack of PE to ELF conversion step. This patch fixes the issue... Signed-off-by: Daniel Kiper Reviewed-by: Ross Philipson Reviewed-by: Alec Brown --- util/grub-pe2elf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/util/grub-pe2elf.c b/util/grub-pe2elf.c index 11331294f..5d8d9c1de 100644 --- a/util/grub-pe2elf.c +++ b/util/grub-pe2elf.c @@ -189,6 +189,7 @@ write_section_data (FILE* fp, const char *name, char *image, shdr[idx_reloc].sh_name = insert_string (relname); shdr[idx_reloc].sh_link = i; + shdr[idx_reloc].sh_flags = SHF_INFO_LINK; shdr[idx_reloc].sh_info = idx; shdr[idx].sh_name = shdr[idx_reloc].sh_name + 4; From e549317e15259ca123f0961643ee5f86879bb551 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Mon, 27 Oct 2025 17:17:01 +0100 Subject: [PATCH 126/208] windows: Fix symbol table generation during module conversion from PE to ELF According to the System V Application Binary Interface specification [1] the sections holding a symbol table, SHT_SYMTAB and SHT_DYNSYM, have to have sh_info set to "One greater than the symbol table index of the last local symbol (binding STB_LOCAL)". Current code converting PE images to ELF files does not do that and readelf complains in following way: ... Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 00000000 000034 0014d4 00 AX 0 0 4 [ 2] .data PROGBITS 00000000 001508 000040 00 WA 0 0 32 [ 3] .rdata PROGBITS 00000000 001548 0006b8 00 A 0 0 4 [ 4] .module_license PROGBITS 00000000 001c00 000010 00 0 0 4 [ 5] .bss NOBITS 00000000 000000 000008 00 WA 0 0 4 [ 6] .moddeps PROGBITS 00000000 001c10 000010 00 0 0 4 [ 7] .modname PROGBITS 00000000 001c20 000008 00 0 0 4 [ 8] .rel.text REL 00000000 001c28 0008c8 08 11 1 4 [ 9] .rel.data REL 00000000 0024f0 000040 08 11 2 4 [10] .rel.rdata REL 00000000 002530 000070 08 11 3 4 [11] .symtab SYMTAB 00000000 0025a0 0001d0 10 12 0 4 [12] .strtab STRTAB 00000000 002770 000237 00 0 0 1 ... Symbol table '.symtab' contains 29 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND readelf: Warning: local symbol 0 found at index >= .symtab's sh_info value of 0 1: 0000144a 0 FUNC LOCAL DEFAULT 1 grub_mod_init readelf: Warning: local symbol 1 found at index >= .symtab's sh_info value of 0 2: 000014aa 0 FUNC LOCAL DEFAULT 1 grub_mod_fini readelf: Warning: local symbol 2 found at index >= .symtab's sh_info value of 0 3: 00000000 0 SECTION LOCAL DEFAULT 1 .text readelf: Warning: local symbol 3 found at index >= .symtab's sh_info value of 0 4: 00000000 0 SECTION LOCAL DEFAULT 2 .data readelf: Warning: local symbol 4 found at index >= .symtab's sh_info value of 0 5: 00000000 0 SECTION LOCAL DEFAULT 5 .bss readelf: Warning: local symbol 5 found at index >= .symtab's sh_info value of 0 6: 00000000 0 SECTION LOCAL DEFAULT 3 .rdata readelf: Warning: local symbol 6 found at index >= .symtab's sh_info value of 0 7: 00000000 0 NOTYPE GLOBAL DEFAULT UND grub_dma_get_phys 8: 00000000 0 NOTYPE GLOBAL DEFAULT UND grub_cs5536_write_msr 9: 00000000 0 NOTYPE GLOBAL DEFAULT UND grub_dma_free ... Let's fix it... [1] https://www.sco.com/developers/gabi/2012-12-31/contents.html Signed-off-by: Daniel Kiper Reviewed-by: Ross Philipson Reviewed-by: Alec Brown --- util/grub-pe2elf.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/util/grub-pe2elf.c b/util/grub-pe2elf.c index 5d8d9c1de..7a3c5a06e 100644 --- a/util/grub-pe2elf.c +++ b/util/grub-pe2elf.c @@ -360,7 +360,7 @@ write_symbol_table (FILE* fp, const char *name, char *image, struct grub_pe32_symbol *pe_symtab; char *pe_strtab; Elf_Sym *symtab; - int *symtab_map, num_syms; + int *symtab_map, num_syms, last_stb_local = 0; int i; pe_symtab = (struct grub_pe32_symbol *) (image + pe_chdr->symtab_offset); @@ -391,7 +391,10 @@ write_symbol_table (FILE* fp, const char *name, char *image, if (pe_symtab->storage_class == GRUB_PE32_SYM_CLASS_EXTERNAL) bind = STB_GLOBAL; else - bind = STB_LOCAL; + { + bind = STB_LOCAL; + last_stb_local = num_syms; + } if ((pe_symtab->type != GRUB_PE32_DT_FUNCTION) && (pe_symtab->num_aux)) { @@ -441,6 +444,7 @@ write_symbol_table (FILE* fp, const char *name, char *image, shdr[symtab_section].sh_size = num_syms * sizeof (Elf_Sym); shdr[symtab_section].sh_entsize = sizeof (Elf_Sym); shdr[symtab_section].sh_link = strtab_section; + shdr[symtab_section].sh_info = ++last_stb_local; shdr[symtab_section].sh_addralign = 4; grub_util_write_image_at (symtab, shdr[symtab_section].sh_size, From 280715ec63a3424f5cf1289302cb4ab4e98768f4 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Tue, 28 Oct 2025 13:19:47 +0100 Subject: [PATCH 127/208] Release 2.14~rc1 Signed-off-by: Daniel Kiper --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 1f391fa8e..8a72c078e 100644 --- a/configure.ac +++ b/configure.ac @@ -34,7 +34,7 @@ dnl "TARGET_" (such as TARGET_CC, TARGET_CFLAGS, etc.) are used for dnl the target type. See INSTALL for full list of variables and dnl description of the relationships between them. -AC_INIT([GRUB],[2.13],[bug-grub@gnu.org]) +AC_INIT([GRUB],[2.14~rc1],[bug-grub@gnu.org]) AS_CASE(["$ERROR_PLATFORM_NOT_SUPPORT_SSP"], [n | no | nO | N | No | NO], [ERROR_PLATFORM_NOT_SUPPORT_SSP=no], From cc36d0f088ef325ca240c6ed4b9e7203d08204e8 Mon Sep 17 00:00:00 2001 From: Lars Wendler Date: Thu, 30 Oct 2025 07:31:59 +0100 Subject: [PATCH 128/208] configure: Avoid bashisms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit or else configure check doesn't succeed with non-bash shell (e.g. dash): checking whether to enable AMD64 as(1) feature detection... /var/tmp/portage/sys-boot/grub-2.14_rc1/work/grub-2.14~rc1/configure: 39176: test: xx86_64: unexpected operator no and later build fails with /var/tmp/portage/sys-boot/grub-2.14_rc1/work/grub-2.14~rc1/grub-core/lib/libgcrypt-grub/src/hwf-x86.c: In function ‘detect_x86_gnuc’: /var/tmp/portage/sys-boot/grub-2.14_rc1/work/grub-2.14~rc1/grub-core/lib/libgcrypt-grub/src/hwf-x86.c:252:17: error: ‘HWF_INTEL_CPU’ undeclared (first use in this function) 252 | result |= HWF_INTEL_CPU; | ^~~~~~~~~~~~~ and other corresponding HWF_INTEL_* definitions because HAVE_CPU_ARCH_X86 was erroneously not defined by configure script. Signed-off-by: Lars Wendler Reviewed-by: Gary Lin Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 8a72c078e..17937baf4 100644 --- a/configure.ac +++ b/configure.ac @@ -1150,7 +1150,7 @@ fi # Implementation of the --disable-amd64-as-feature-detection switch. AC_MSG_CHECKING([whether to enable AMD64 as(1) feature detection]) -if test x$target_cpu == xx86_64 -a x$platform == xefi; then +if test x$target_cpu = xx86_64 -a x$platform = xefi; then CPPFLAGS_GCRY_ASM="-D__x86_64 -DHAVE_CPU_ARCH_X86" AC_ARG_ENABLE(amd64-as-feature-detection, AS_HELP_STRING([--disable-amd64-as-feature-detection], From 5f0ffd7f575447aab4c5bb8cfc08961971e9e767 Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Tue, 28 Oct 2025 21:58:51 +0530 Subject: [PATCH 129/208] term/ieee1275/serial: Fix memory leak The grub_zalloc() allocates memory for port. If the allocation for port->name fails the function returns NULL without freeing the previously allocated port memory. This results in a memory leak. To avoid this we must free port before return. Signed-off-by: Avnish Chouhan Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/term/ieee1275/serial.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/grub-core/term/ieee1275/serial.c b/grub-core/term/ieee1275/serial.c index ac2a8f827..b3a8a7c8c 100644 --- a/grub-core/term/ieee1275/serial.c +++ b/grub-core/term/ieee1275/serial.c @@ -236,7 +236,11 @@ add_port (struct ofserial_hash_ent *ent) + grub_strlen (ent->shortest)); port->elem = ent; if (!port->name) - return NULL; + { + grub_free (port); + return NULL; + } + ptr = grub_stpcpy (port->name, "ieee1275/"); grub_strcpy (ptr, ent->shortest); From c0d2f76d55fcbf433b3525a3f3c4aa7d697b1105 Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Tue, 28 Oct 2025 22:01:59 +0530 Subject: [PATCH 130/208] normal/completion: Add missing grub_malloc() failure check This patch adds a NULL check after grub_malloc() call. Missing a failure check after calling grub_malloc() can lead to undefined behavior. If the allocation fails and returns NULL subsequent dereferencing or writing to the pointer will likely result in a runtime error such as a segmentation fault. Signed-off-by: Avnish Chouhan Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/normal/completion.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/normal/completion.c b/grub-core/normal/completion.c index 18cadfa85..4058e0a62 100644 --- a/grub-core/normal/completion.c +++ b/grub-core/normal/completion.c @@ -490,6 +490,9 @@ grub_normal_do_completion (char *buf, int *restore, spaces++; ret = grub_malloc (match_len - current_len + grub_strlen (suffix) + spaces + 1); + if (ret == NULL) + goto fail; + newstr = ret; for (escstr = match + current_len; *escstr; escstr++) { From 4cc22937e780f22d7b469d2da8df701957913de7 Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Tue, 28 Oct 2025 22:02:00 +0530 Subject: [PATCH 131/208] partmap/msdos: Add missing grub_malloc() failure check This patch adds a NULL check after grub_malloc() call. Missing a failure check after calling grub_malloc() can lead to undefined behavior. If the allocation fails and returns NULL subsequent dereferencing or writing to the pointer will likely result in a runtime error such as a segmentation fault. Signed-off-by: Avnish Chouhan Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/partmap/msdos.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/partmap/msdos.c b/grub-core/partmap/msdos.c index c85bb74be..bf926175e 100644 --- a/grub-core/partmap/msdos.c +++ b/grub-core/partmap/msdos.c @@ -348,6 +348,9 @@ pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors, * area. */ embed_signature_check = grub_malloc (GRUB_DISK_SECTOR_SIZE); + if (embed_signature_check == NULL) + return grub_errno; + for (i = 0; i < *nsectors; i++) { if (grub_disk_read (disk, (*sectors)[i], 0, GRUB_DISK_SECTOR_SIZE, From 77e2ceb79a240d54445355317d2f4569c0550ae2 Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Tue, 28 Oct 2025 22:02:01 +0530 Subject: [PATCH 132/208] mmap/mmap: Add missing grub_malloc() failure check This patch adds a NULL check after grub_malloc() call. Missing a failure check after calling grub_malloc() can lead to undefined behavior. If the allocation fails and returns NULL subsequent dereferencing or writing to the pointer will likely result in a runtime error such as a segmentation fault. Signed-off-by: Avnish Chouhan Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/mmap/mmap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/mmap/mmap.c b/grub-core/mmap/mmap.c index c8c8312c5..8f03b7765 100644 --- a/grub-core/mmap/mmap.c +++ b/grub-core/mmap/mmap.c @@ -242,6 +242,9 @@ grub_mmap_iterate (grub_memory_hook_t hook, void *hook_data) else { struct mm_list *n = grub_malloc (sizeof (*n)); + if (n == NULL) + return grub_errno; + n->val = ctx.scanline_events[i].memtype; n->present = 1; n->next = present[ctx.scanline_events[i].priority].next; From d4f476f08e66aded4c8d99628280c9de14befd64 Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Tue, 28 Oct 2025 22:02:02 +0530 Subject: [PATCH 133/208] lib/legacy_parse: Add missing grub_malloc() failure check This patch adds a NULL check after grub_malloc() call. Missing a failure check after calling grub_malloc() can lead to undefined behavior. If the allocation fails and returns NULL subsequent dereferencing or writing to the pointer will likely result in a runtime error such as a segmentation fault. Signed-off-by: Avnish Chouhan Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/lib/legacy_parse.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/lib/legacy_parse.c b/grub-core/lib/legacy_parse.c index fa0131a1e..899530944 100644 --- a/grub-core/lib/legacy_parse.c +++ b/grub-core/lib/legacy_parse.c @@ -508,6 +508,9 @@ grub_legacy_parse (const char *buf, char **entryname, char **suffix) char *ret; int len = grub_strlen (buf); ret = grub_malloc (len + 2); + if (ret == NULL) + return NULL; + grub_memcpy (ret, buf, len); if (len && ret[len - 1] == '\n') ret[len] = 0; From d9b7b23d2d90922ea82ceceebb283bc16f85cd4b Mon Sep 17 00:00:00 2001 From: Srish Srinivasan Date: Mon, 29 Sep 2025 16:59:34 +0530 Subject: [PATCH 134/208] tests: Extend pbkdf2_test to cover HMAC-SHA{256,512} HMAC-SHA1 is the only HMAC variant tested in the existing vectors. Add vectors to test HMAC-SHA{256,512} as well. Signed-off-by: Srish Srinivasan Reviewed-by: Stefan Berger Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/tests/pbkdf2_test.c | 89 ++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/grub-core/tests/pbkdf2_test.c b/grub-core/tests/pbkdf2_test.c index 4eeb8b89f..ba735d4ec 100644 --- a/grub-core/tests/pbkdf2_test.c +++ b/grub-core/tests/pbkdf2_test.c @@ -31,6 +31,7 @@ static struct grub_size_t Slen; unsigned int c; grub_size_t dkLen; + const gcry_md_spec_t *HMAC_variant; const char *DK; } vectors[] = { /* RFC6070. */ @@ -38,6 +39,7 @@ static struct "password", 8, "salt", 4, 1, 20, + GRUB_MD_SHA1, "\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9\xb5\x24\xaf\x60\x12" "\x06\x2f\xe0\x37\xa6" }, @@ -45,6 +47,7 @@ static struct "password", 8, "salt", 4, 2, 20, + GRUB_MD_SHA1, "\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c" "\xcd\x1e\xd9\x2a\xce\x1d\x41\xf0" "\xd8\xde\x89\x57" @@ -53,6 +56,7 @@ static struct "password", 8, "salt", 4, 4096, 20, + GRUB_MD_SHA1, "\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad\x49\xd9\x26\xf7" "\x21\xd0\x65\xa4\x29\xc1" }, @@ -60,6 +64,7 @@ static struct "passwordPASSWORDpassword", 24, "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36, 4096, 25, + GRUB_MD_SHA1, "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8\xd8\x36\x62\xc0" "\xe4\x4a\x8b\x29\x1a\x96\x4c\xf2\xf0\x70\x38" }, @@ -67,7 +72,89 @@ static struct "pass\0word", 9, "sa\0lt", 5, 4096, 16, + GRUB_MD_SHA1, "\x56\xfa\x6a\xa7\x55\x48\x09\x9d\xcc\x37\xd7\xf0\x34\x25\xe0\xc3" + }, + /* Re-using the above vectors for HMAC-SHA{256,512} */ + { + "password", 8, + "salt", 4, + 1, 20, + GRUB_MD_SHA256, + "\x12\x0f\xb6\xcf\xfc\xf8\xb3\x2c\x43\xe7\x22\x52\x56\xc4\xf8" + "\x37\xa8\x65\x48\xc9" + }, + { + "password", 8, + "salt", 4, + 2, 20, + GRUB_MD_SHA256, + "\xae\x4d\x0c\x95\xaf\x6b\x46\xd3" + "\x2d\x0a\xdf\xf9\x28\xf0\x6d\xd0" + "\x2a\x30\x3f\x8e" + }, + { + "password", 8, + "salt", 4, + 4096, 20, + GRUB_MD_SHA256, + "\xc5\xe4\x78\xd5\x92\x88\xc8\x41\xaa\x53\x0d\xb6\x84\x5c" + "\x4c\x8d\x96\x28\x93\xa0" + }, + { + "passwordPASSWORDpassword", 24, + "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36, + 4096, 25, + GRUB_MD_SHA256, + "\x34\x8c\x89\xdb\xcb\xd3\x2b\x2f\x32\xd8\x14\xb8\x11\x6e" + "\x84\xcf\x2b\x17\x34\x7e\xbc\x18\x00\x18\x1c" + }, + { + "pass\0word", 9, + "sa\0lt", 5, + 4096, 16, + GRUB_MD_SHA256, + "\x89\xb6\x9d\x05\x16\xf8\x29\x89\x3c\x69\x62\x26\x65\x0a\x86\x87" + }, + { + "password", 8, + "salt", 4, + 1, 20, + GRUB_MD_SHA512, + "\x86\x7f\x70\xcf\x1a\xde\x02\xcf\xf3\x75\x25\x99\xa3\xa5\x3d" + "\xc4\xaf\x34\xc7\xa6" + }, + { + "password", 8, + "salt", 4, + 2, 20, + GRUB_MD_SHA512, + "\xe1\xd9\xc1\x6a\xa6\x81\x70\x8a" + "\x45\xf5\xc7\xc4\xe2\x15\xce\xb6" + "\x6e\x01\x1a\x2e" + }, + { + "password", 8, + "salt", 4, + 4096, 20, + GRUB_MD_SHA512, + "\xd1\x97\xb1\xb3\x3d\xb0\x14\x3e\x01\x8b\x12\xf3\xd1\xd1" + "\x47\x9e\x6c\xde\xbd\xcc" + }, + { + "passwordPASSWORDpassword", 24, + "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36, + 4096, 25, + GRUB_MD_SHA512, + "\x8c\x05\x11\xf4\xc6\xe5\x97\xc6\xac\x63\x15\xd8\xf0\x36" + "\x2e\x22\x5f\x3c\x50\x14\x95\xba\x23\xb8\x68" + }, + { + "pass\0word", 9, + "sa\0lt", 5, + 4096, 16, + GRUB_MD_SHA512, + "\x9d\x9e\x9c\x4c\xd2\x1f\xe4\xbe\x24\xd5\xb8\x24\x4c\x75\x96\x65" } }; @@ -80,7 +167,7 @@ pbkdf2_test (void) { gcry_err_code_t err; grub_uint8_t DK[32]; - err = grub_crypto_pbkdf2 (GRUB_MD_SHA1, + err = grub_crypto_pbkdf2 (vectors[i].HMAC_variant, (const grub_uint8_t *) vectors[i].P, vectors[i].Plen, (const grub_uint8_t *) vectors[i].S, From bf13fed5fee963a7ec676baecf02684310a07ddf Mon Sep 17 00:00:00 2001 From: Leo Sandoval Date: Fri, 19 Sep 2025 15:45:03 -0600 Subject: [PATCH 135/208] tests: Skip tests if required tools are not available There is no reason to fail a test if the required testing tool is not present on the system, so skip the test instead of failing it. Signed-off-by: Leo Sandoval Reviewed-by: Andrew Hamilton Reviewed-by: Neal Gompa Reviewed-by: Daniel Kiper --- tests/btrfs_test.in | 2 +- tests/cpio_test.in | 2 +- tests/erofs_test.in | 2 +- tests/exfat_test.in | 2 +- tests/ext234_test.in | 6 +++--- tests/f2fs_test.in | 2 +- tests/fat_test.in | 2 +- tests/grub_cmd_cryptomount.in | 4 ++-- tests/gzcompress_test.in | 2 +- tests/hfs_test.in | 2 +- tests/hfsplus_test.in | 2 +- tests/iso9660_test.in | 2 +- tests/jfs_test.in | 2 +- tests/luks1_test.in | 4 ++-- tests/luks2_test.in | 4 ++-- tests/lzocompress_test.in | 2 +- tests/minixfs_test.in | 2 +- tests/nilfs2_test.in | 2 +- tests/ntfs_test.in | 4 ++-- tests/partmap_test.in | 2 +- tests/reiserfs_test.in | 2 +- tests/romfs_test.in | 2 +- tests/squashfs_test.in | 2 +- tests/tar_test.in | 2 +- tests/tpm2_key_protector_test.in | 4 ++-- tests/udf_test.in | 2 +- tests/xfs_test.in | 2 +- tests/xzcompress_test.in | 2 +- tests/zfs_test.in | 2 +- 29 files changed, 36 insertions(+), 36 deletions(-) diff --git a/tests/btrfs_test.in b/tests/btrfs_test.in index 0d098c9a2..a07d2e5d1 100644 --- a/tests/btrfs_test.in +++ b/tests/btrfs_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.btrfs >/dev/null 2>&1; then echo "mkfs.btrfs not installed; cannot test btrfs." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" btrfs diff --git a/tests/cpio_test.in b/tests/cpio_test.in index e2e668cf6..5742cf17b 100644 --- a/tests/cpio_test.in +++ b/tests/cpio_test.in @@ -4,7 +4,7 @@ set -e if ! which cpio >/dev/null 2>&1; then echo "cpio not installed; cannot test cpio." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" cpio_bin diff --git a/tests/erofs_test.in b/tests/erofs_test.in index 51111627a..436468dac 100644 --- a/tests/erofs_test.in +++ b/tests/erofs_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.erofs >/dev/null 2>&1; then echo "mkfs.erofs not installed; cannot test erofs." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" erofs_compact diff --git a/tests/exfat_test.in b/tests/exfat_test.in index 7939f25d2..7acde1977 100644 --- a/tests/exfat_test.in +++ b/tests/exfat_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.exfat >/dev/null 2>&1; then echo "mkfs.exfat not installed; cannot test exFAT." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" exfat diff --git a/tests/ext234_test.in b/tests/ext234_test.in index 4df696710..c9a8c1056 100644 --- a/tests/ext234_test.in +++ b/tests/ext234_test.in @@ -12,17 +12,17 @@ fi if ! which mkfs.ext2 >/dev/null 2>&1; then echo "mkfs.ext2 not installed; cannot test ext2." - exit 99 + exit 77 fi if ! which mkfs.ext3 >/dev/null 2>&1; then echo "mkfs.ext3 not installed; cannot test ext3." - exit 99 + exit 77 fi if ! which mkfs.ext4 >/dev/null 2>&1; then echo "mkfs.ext4 not installed; cannot test ext4." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" ext2_old diff --git a/tests/f2fs_test.in b/tests/f2fs_test.in index 85f8cc8bc..a020a0f96 100644 --- a/tests/f2fs_test.in +++ b/tests/f2fs_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.f2fs >/dev/null 2>&1; then echo "mkfs.f2fs not installed; cannot test f2fs." - exit 99 + exit 77 fi diff --git a/tests/fat_test.in b/tests/fat_test.in index 8a2b37c5c..77e3f16c6 100644 --- a/tests/fat_test.in +++ b/tests/fat_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.vfat >/dev/null 2>&1; then echo "mkfs.vfat not installed; cannot test FAT." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" vfat16a diff --git a/tests/grub_cmd_cryptomount.in b/tests/grub_cmd_cryptomount.in index eaa187efa..25d7d8c36 100644 --- a/tests/grub_cmd_cryptomount.in +++ b/tests/grub_cmd_cryptomount.in @@ -27,12 +27,12 @@ fi if ! which cryptsetup >/dev/null 2>&1; then echo "cryptsetup not installed; cannot test cryptomount." - exit 99 + exit 77 fi if ! which mkfs.vfat >/dev/null 2>&1; then echo "mkfs.vfat not installed; cannot test cryptomount." - exit 99 + exit 77 fi COMMON_OPTS='${V:+--debug=$V} --cs-opts="--pbkdf-force-iterations 1000"' diff --git a/tests/gzcompress_test.in b/tests/gzcompress_test.in index 8e7e6a633..d7a594bb2 100644 --- a/tests/gzcompress_test.in +++ b/tests/gzcompress_test.in @@ -21,7 +21,7 @@ grubshell=@builddir@/grub-shell if ! which gzip >/dev/null 2>&1; then echo "gzip not installed; cannot test gzip compression." - exit 99 + exit 77 fi v=$(echo hello | "${grubshell}" --mkrescue-arg=--compress=gz) diff --git a/tests/hfs_test.in b/tests/hfs_test.in index 960f1cbd0..c916b9bac 100644 --- a/tests/hfs_test.in +++ b/tests/hfs_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.hfs >/dev/null 2>&1; then echo "mkfs.hfs not installed; cannot test HFS." - exit 99 + exit 77 fi if ! grep -q mac_roman /proc/modules && ! modprobe mac_roman; then diff --git a/tests/hfsplus_test.in b/tests/hfsplus_test.in index f727cf0e2..cb36a3661 100644 --- a/tests/hfsplus_test.in +++ b/tests/hfsplus_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.hfsplus >/dev/null 2>&1; then echo "mkfs.hfsplus not installed; cannot test hfsplus." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" hfsplus diff --git a/tests/iso9660_test.in b/tests/iso9660_test.in index a1f752adf..793664c87 100644 --- a/tests/iso9660_test.in +++ b/tests/iso9660_test.in @@ -4,7 +4,7 @@ set -e if ! which xorriso >/dev/null 2>&1; then echo "xorriso not installed; cannot test iso9660." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" joliet diff --git a/tests/jfs_test.in b/tests/jfs_test.in index d13780e23..86f9ebeaa 100644 --- a/tests/jfs_test.in +++ b/tests/jfs_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.jfs >/dev/null 2>&1; then echo "mkfs.jfs not installed; cannot test JFS." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" jfs diff --git a/tests/luks1_test.in b/tests/luks1_test.in index cd28fd714..0bfb53c98 100644 --- a/tests/luks1_test.in +++ b/tests/luks1_test.in @@ -12,12 +12,12 @@ fi if ! which mkfs.ext2 >/dev/null 2>&1; then echo "mkfs.ext2 not installed; cannot test luks." - exit 99 + exit 77 fi if ! which cryptsetup >/dev/null 2>&1; then echo "cryptsetup not installed; cannot test luks." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" luks1 diff --git a/tests/luks2_test.in b/tests/luks2_test.in index 6a26ba626..49db47380 100644 --- a/tests/luks2_test.in +++ b/tests/luks2_test.in @@ -12,12 +12,12 @@ fi if ! which mkfs.ext2 >/dev/null 2>&1; then echo "mkfs.ext2 not installed; cannot test luks2." - exit 99 + exit 77 fi if ! which cryptsetup >/dev/null 2>&1; then echo "cryptsetup not installed; cannot test luks2." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" luks2 diff --git a/tests/lzocompress_test.in b/tests/lzocompress_test.in index 915f74bd9..42e270df0 100644 --- a/tests/lzocompress_test.in +++ b/tests/lzocompress_test.in @@ -21,7 +21,7 @@ grubshell=@builddir@/grub-shell if ! which lzop >/dev/null 2>&1; then echo "lzop not installed; cannot test lzo compression." - exit 99 + exit 77 fi v=$(echo hello | "${grubshell}" --mkrescue-arg=--compress=lzo) diff --git a/tests/minixfs_test.in b/tests/minixfs_test.in index c62f56c8b..78577fec8 100644 --- a/tests/minixfs_test.in +++ b/tests/minixfs_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.minix >/dev/null 2>&1; then echo "mkfs.minix not installed; cannot test minixfs." - exit 99 + exit 77 fi if ! mkfs.minix -h | grep -- -v > /dev/null; then diff --git a/tests/nilfs2_test.in b/tests/nilfs2_test.in index 8cc93754c..719972f53 100644 --- a/tests/nilfs2_test.in +++ b/tests/nilfs2_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.nilfs2 >/dev/null 2>&1; then echo "mkfs.nilfs2 not installed; cannot test nilfs2." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" nilfs2 diff --git a/tests/ntfs_test.in b/tests/ntfs_test.in index c2b08d27f..da73c59f9 100644 --- a/tests/ntfs_test.in +++ b/tests/ntfs_test.in @@ -12,12 +12,12 @@ fi if ! which mkfs.ntfs >/dev/null 2>&1; then echo "mkfs.ntfs not installed; cannot test ntfs." - exit 99 + exit 77 fi if ! which setfattr >/dev/null 2>&1; then echo "setfattr not installed; cannot test ntfs." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" ntfs diff --git a/tests/partmap_test.in b/tests/partmap_test.in index 4138e88fe..9810cc1ac 100644 --- a/tests/partmap_test.in +++ b/tests/partmap_test.in @@ -100,7 +100,7 @@ esac if ! which ${parted} >/dev/null 2>&1; then echo "${parted} not installed; cannot test partmap" - exit 99 + exit 77 fi imgfile="`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 99 diff --git a/tests/reiserfs_test.in b/tests/reiserfs_test.in index 37226c01b..36e34c305 100644 --- a/tests/reiserfs_test.in +++ b/tests/reiserfs_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.reiserfs >/dev/null 2>&1; then echo "mkfs.reiserfs not installed; cannot test reiserfs." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" reiserfs diff --git a/tests/romfs_test.in b/tests/romfs_test.in index f968e9b7d..98bb50c32 100644 --- a/tests/romfs_test.in +++ b/tests/romfs_test.in @@ -4,7 +4,7 @@ set -e if ! which genromfs >/dev/null 2>&1; then echo "genromfs not installed; cannot test romfs." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" romfs diff --git a/tests/squashfs_test.in b/tests/squashfs_test.in index 15e70218f..2f044f95d 100644 --- a/tests/squashfs_test.in +++ b/tests/squashfs_test.in @@ -4,7 +4,7 @@ set -e if ! which mksquashfs >/dev/null 2>&1; then echo "mksquashfs not installed; cannot test squashfs." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" squash4_gzip diff --git a/tests/tar_test.in b/tests/tar_test.in index 97944b243..6e2f2de8b 100644 --- a/tests/tar_test.in +++ b/tests/tar_test.in @@ -4,7 +4,7 @@ set -e if ! which tar >/dev/null 2>&1; then echo "tar not installed; cannot test tar." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" tarfs diff --git a/tests/tpm2_key_protector_test.in b/tests/tpm2_key_protector_test.in index 5dd86d6ee..6dbd69787 100644 --- a/tests/tpm2_key_protector_test.in +++ b/tests/tpm2_key_protector_test.in @@ -41,7 +41,7 @@ fi if ! command -v cryptsetup >/dev/null 2>&1; then echo "cryptsetup not installed; cannot test tpm2." - exit 99 + exit 77 fi if ! grep -q tpm_vtpm_proxy /proc/modules && ! modprobe tpm_vtpm_proxy; then @@ -51,7 +51,7 @@ fi if ! command -v swtpm >/dev/null 2>&1; then echo "swtpm not installed; cannot test tpm2." - exit 99 + exit 77 fi if ! command -v tpm2_startup >/dev/null 2>&1; then diff --git a/tests/udf_test.in b/tests/udf_test.in index 302b28ab2..8968fb103 100644 --- a/tests/udf_test.in +++ b/tests/udf_test.in @@ -12,7 +12,7 @@ fi if ! which mkudffs >/dev/null 2>&1; then echo "mkudffs not installed; cannot test UDF." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" udf diff --git a/tests/xfs_test.in b/tests/xfs_test.in index 5e029c182..8a648aa4a 100644 --- a/tests/xfs_test.in +++ b/tests/xfs_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.xfs >/dev/null 2>&1; then echo "mkfs.xfs not installed; cannot test xfs." - exit 99 + exit 77 fi diff --git a/tests/xzcompress_test.in b/tests/xzcompress_test.in index 6ef73e41e..cfc6ccba6 100644 --- a/tests/xzcompress_test.in +++ b/tests/xzcompress_test.in @@ -21,7 +21,7 @@ grubshell=@builddir@/grub-shell if ! which xz >/dev/null 2>&1; then echo "xz not installed; cannot test xz compression." - exit 99 + exit 77 fi v=$(echo hello | "${grubshell}" --mkrescue-arg=--compress=xz) diff --git a/tests/zfs_test.in b/tests/zfs_test.in index 0d0a57f7d..c8bb7937f 100644 --- a/tests/zfs_test.in +++ b/tests/zfs_test.in @@ -12,7 +12,7 @@ fi if ! which zpool >/dev/null 2>&1; then echo "zpool not installed; cannot test zfs." - exit 99 + exit 77 fi "@builddir@/grub-fs-tester" zfs From cdbc6ffbb85778fb843dadf97699884dc2447a5a Mon Sep 17 00:00:00 2001 From: Leo Sandoval Date: Fri, 19 Sep 2025 15:45:04 -0600 Subject: [PATCH 136/208] tests: Increase verbosity in *_test.in checks In this case it does not hurt to increase bash execution verbosity so we can get more insight in case of issues. Signed-off-by: Leo Sandoval Reviewed-by: Daniel Kiper --- tests/ahci_test.in | 2 +- tests/asn1_test.in | 2 +- tests/btrfs_test.in | 2 +- tests/cdboot_test.in | 2 +- tests/core_compress_test.in | 2 +- tests/cpio_test.in | 2 +- tests/ehci_test.in | 2 +- tests/erofs_test.in | 2 +- tests/example_scripted_test.in | 2 +- tests/exfat_test.in | 2 +- tests/ext234_test.in | 2 +- tests/f2fs_test.in | 2 +- tests/fat_test.in | 2 +- tests/fddboot_test.in | 2 +- tests/file_filter_test.in | 2 +- tests/grub_cmd_date.in | 2 +- tests/grub_cmd_regexp.in | 2 +- tests/grub_cmd_set_date.in | 2 +- tests/grub_cmd_sleep.in | 2 +- tests/grub_cmd_test.in | 2 +- tests/grub_func_test.in | 2 +- tests/grub_script_blanklines.in | 2 +- tests/grub_script_blockarg.in | 2 +- tests/grub_script_dollar.in | 2 +- tests/grub_script_expansion.in | 2 +- tests/grub_script_final_semicolon.in | 2 +- tests/grub_script_no_commands.in | 2 +- tests/gzcompress_test.in | 2 +- tests/hddboot_test.in | 2 +- tests/help_test.in | 2 +- tests/hfs_test.in | 2 +- tests/hfsplus_test.in | 2 +- tests/iso9660_test.in | 2 +- tests/jfs_test.in | 2 +- tests/luks1_test.in | 2 +- tests/luks2_test.in | 2 +- tests/lzocompress_test.in | 2 +- tests/minixfs_test.in | 2 +- tests/netboot_test.in | 2 +- tests/nilfs2_test.in | 2 +- tests/ntfs_test.in | 2 +- tests/ohci_test.in | 2 +- tests/partmap_test.in | 2 +- tests/pata_test.in | 2 +- tests/pseries_test.in | 2 +- tests/reiserfs_test.in | 2 +- tests/romfs_test.in | 2 +- tests/serial_test.in | 2 +- tests/squashfs_test.in | 2 +- tests/syslinux_test.in | 2 +- tests/tar_test.in | 2 +- tests/test_sha512sum.in | 2 +- tests/udf_test.in | 2 +- tests/uhci_test.in | 2 +- tests/xfs_test.in | 2 +- tests/xzcompress_test.in | 2 +- tests/zfs_test.in | 2 +- 57 files changed, 57 insertions(+), 57 deletions(-) diff --git a/tests/ahci_test.in b/tests/ahci_test.in index 70646a24e..ffb7cf48d 100644 --- a/tests/ahci_test.in +++ b/tests/ahci_test.in @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with GRUB. If not, see . -set -e +set -ex grubshell=@builddir@/grub-shell . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/asn1_test.in b/tests/asn1_test.in index 8f18ee6bb..a9b82dd0e 100644 --- a/tests/asn1_test.in +++ b/tests/asn1_test.in @@ -1,5 +1,5 @@ #! @BUILD_SHEBANG@ -set -e +set -ex . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/btrfs_test.in b/tests/btrfs_test.in index a07d2e5d1..84cfd95d8 100644 --- a/tests/btrfs_test.in +++ b/tests/btrfs_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` diff --git a/tests/cdboot_test.in b/tests/cdboot_test.in index f00cdec58..ec8943b16 100644 --- a/tests/cdboot_test.in +++ b/tests/cdboot_test.in @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with GRUB. If not, see . -set -e +set -ex grubshell=@builddir@/grub-shell . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/core_compress_test.in b/tests/core_compress_test.in index 24a811418..0b97c9557 100644 --- a/tests/core_compress_test.in +++ b/tests/core_compress_test.in @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with GRUB. If not, see . -set -e +set -ex grubshell=@builddir@/grub-shell . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/cpio_test.in b/tests/cpio_test.in index 5742cf17b..fb468564a 100644 --- a/tests/cpio_test.in +++ b/tests/cpio_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if ! which cpio >/dev/null 2>&1; then echo "cpio not installed; cannot test cpio." diff --git a/tests/ehci_test.in b/tests/ehci_test.in index bf823a5de..7aff6df94 100644 --- a/tests/ehci_test.in +++ b/tests/ehci_test.in @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with GRUB. If not, see . -set -e +set -ex grubshell=@builddir@/grub-shell . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/erofs_test.in b/tests/erofs_test.in index 436468dac..e3f3ea5eb 100644 --- a/tests/erofs_test.in +++ b/tests/erofs_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` diff --git a/tests/example_scripted_test.in b/tests/example_scripted_test.in index 783b7f138..0f32c79fa 100644 --- a/tests/example_scripted_test.in +++ b/tests/example_scripted_test.in @@ -1,4 +1,4 @@ #!@BUILD_SHEBANG@ -set -e +set -ex true diff --git a/tests/exfat_test.in b/tests/exfat_test.in index 7acde1977..ae2b209a1 100644 --- a/tests/exfat_test.in +++ b/tests/exfat_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` diff --git a/tests/ext234_test.in b/tests/ext234_test.in index c9a8c1056..2f8aafc9c 100644 --- a/tests/ext234_test.in +++ b/tests/ext234_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` diff --git a/tests/f2fs_test.in b/tests/f2fs_test.in index a020a0f96..4d16c2fab 100644 --- a/tests/f2fs_test.in +++ b/tests/f2fs_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` diff --git a/tests/fat_test.in b/tests/fat_test.in index 77e3f16c6..0e4b1ebae 100644 --- a/tests/fat_test.in +++ b/tests/fat_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` diff --git a/tests/fddboot_test.in b/tests/fddboot_test.in index 6ef49efcb..74d2bcd87 100644 --- a/tests/fddboot_test.in +++ b/tests/fddboot_test.in @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with GRUB. If not, see . -set -e +set -ex grubshell=@builddir@/grub-shell . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/file_filter_test.in b/tests/file_filter_test.in index 18324f1a0..54fcf8e88 100644 --- a/tests/file_filter_test.in +++ b/tests/file_filter_test.in @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with GRUB. If not, see . -set -e +set -ex grubshell=@builddir@/grub-shell . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/grub_cmd_date.in b/tests/grub_cmd_date.in index 4903ad6cc..359c624fc 100644 --- a/tests/grub_cmd_date.in +++ b/tests/grub_cmd_date.in @@ -1,5 +1,5 @@ #! @BUILD_SHEBANG@ -set -e +set -ex . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/grub_cmd_regexp.in b/tests/grub_cmd_regexp.in index 6520bd6d7..13633bb05 100644 --- a/tests/grub_cmd_regexp.in +++ b/tests/grub_cmd_regexp.in @@ -1,5 +1,5 @@ #! @BUILD_SHEBANG@ -set -e +set -ex # Run GRUB script in a Qemu instance # Copyright (C) 2010 Free Software Foundation, Inc. diff --git a/tests/grub_cmd_set_date.in b/tests/grub_cmd_set_date.in index 17673cd8a..5572535fa 100644 --- a/tests/grub_cmd_set_date.in +++ b/tests/grub_cmd_set_date.in @@ -1,5 +1,5 @@ #! @BUILD_SHEBANG@ -set -e +set -ex . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/grub_cmd_sleep.in b/tests/grub_cmd_sleep.in index 1a57fb388..2da9d722e 100644 --- a/tests/grub_cmd_sleep.in +++ b/tests/grub_cmd_sleep.in @@ -1,5 +1,5 @@ #! @BUILD_SHEBANG@ -set -e +set -ex . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/grub_cmd_test.in b/tests/grub_cmd_test.in index 043c3a634..a78a5d6ee 100644 --- a/tests/grub_cmd_test.in +++ b/tests/grub_cmd_test.in @@ -1,5 +1,5 @@ #! @BUILD_SHEBANG@ -set -e +set -ex # create a randome file empty="`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 99 diff --git a/tests/grub_func_test.in b/tests/grub_func_test.in index 1fa3c4352..6065ff6df 100644 --- a/tests/grub_func_test.in +++ b/tests/grub_func_test.in @@ -1,5 +1,5 @@ #! @BUILD_SHEBANG@ -set -e +set -ex . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/grub_script_blanklines.in b/tests/grub_script_blanklines.in index bd8735491..9b84782c3 100644 --- a/tests/grub_script_blanklines.in +++ b/tests/grub_script_blanklines.in @@ -1,5 +1,5 @@ #! @BUILD_SHEBANG@ -set -e +set -ex @builddir@/grub-script-check <. -set -e +set -ex grubshell=@builddir@/grub-shell . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/hddboot_test.in b/tests/hddboot_test.in index 764e0da1c..76704c379 100644 --- a/tests/hddboot_test.in +++ b/tests/hddboot_test.in @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with GRUB. If not, see . -set -e +set -ex grubshell=@builddir@/grub-shell . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/help_test.in b/tests/help_test.in index 9c8ca52c8..ca3d7f31b 100644 --- a/tests/help_test.in +++ b/tests/help_test.in @@ -1,5 +1,5 @@ #! @BUILD_SHEBANG@ -set -e +set -ex . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/hfs_test.in b/tests/hfs_test.in index c916b9bac..fc0e0e75c 100644 --- a/tests/hfs_test.in +++ b/tests/hfs_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` diff --git a/tests/hfsplus_test.in b/tests/hfsplus_test.in index cb36a3661..7918a68bb 100644 --- a/tests/hfsplus_test.in +++ b/tests/hfsplus_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` diff --git a/tests/iso9660_test.in b/tests/iso9660_test.in index 793664c87..7a6816f94 100644 --- a/tests/iso9660_test.in +++ b/tests/iso9660_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if ! which xorriso >/dev/null 2>&1; then echo "xorriso not installed; cannot test iso9660." diff --git a/tests/jfs_test.in b/tests/jfs_test.in index 86f9ebeaa..b1c18e9b1 100644 --- a/tests/jfs_test.in +++ b/tests/jfs_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` diff --git a/tests/luks1_test.in b/tests/luks1_test.in index 0bfb53c98..3344746c1 100644 --- a/tests/luks1_test.in +++ b/tests/luks1_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` diff --git a/tests/luks2_test.in b/tests/luks2_test.in index 49db47380..5368698f8 100644 --- a/tests/luks2_test.in +++ b/tests/luks2_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` diff --git a/tests/lzocompress_test.in b/tests/lzocompress_test.in index 42e270df0..50145dd0c 100644 --- a/tests/lzocompress_test.in +++ b/tests/lzocompress_test.in @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with GRUB. If not, see . -set -e +set -ex grubshell=@builddir@/grub-shell . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/minixfs_test.in b/tests/minixfs_test.in index 78577fec8..0d28a7226 100644 --- a/tests/minixfs_test.in +++ b/tests/minixfs_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` diff --git a/tests/netboot_test.in b/tests/netboot_test.in index 510c9c34b..b8a5f9abb 100644 --- a/tests/netboot_test.in +++ b/tests/netboot_test.in @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with GRUB. If not, see . -set -e +set -ex grubshell=@builddir@/grub-shell . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/nilfs2_test.in b/tests/nilfs2_test.in index 719972f53..84b8b1a0b 100644 --- a/tests/nilfs2_test.in +++ b/tests/nilfs2_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` diff --git a/tests/ntfs_test.in b/tests/ntfs_test.in index da73c59f9..5b1bebe16 100644 --- a/tests/ntfs_test.in +++ b/tests/ntfs_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` diff --git a/tests/ohci_test.in b/tests/ohci_test.in index a40d3bc0a..1652b7efc 100644 --- a/tests/ohci_test.in +++ b/tests/ohci_test.in @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with GRUB. If not, see . -set -e +set -ex grubshell=@builddir@/grub-shell . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/partmap_test.in b/tests/partmap_test.in index 9810cc1ac..8e12cf7ec 100644 --- a/tests/partmap_test.in +++ b/tests/partmap_test.in @@ -1,5 +1,5 @@ #! @BUILD_SHEBANG@ -set -e +set -ex # Copyright (C) 2010 Free Software Foundation, Inc. # diff --git a/tests/pata_test.in b/tests/pata_test.in index 4d0e7d573..6c2fa43dd 100644 --- a/tests/pata_test.in +++ b/tests/pata_test.in @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with GRUB. If not, see . -set -e +set -ex grubshell=@builddir@/grub-shell . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/pseries_test.in b/tests/pseries_test.in index 9b4090cf5..a1dd1670a 100644 --- a/tests/pseries_test.in +++ b/tests/pseries_test.in @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with GRUB. If not, see . -set -e +set -ex grubshell=@builddir@/grub-shell . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/reiserfs_test.in b/tests/reiserfs_test.in index 36e34c305..48d3ab4cc 100644 --- a/tests/reiserfs_test.in +++ b/tests/reiserfs_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` diff --git a/tests/romfs_test.in b/tests/romfs_test.in index 98bb50c32..2c66cde34 100644 --- a/tests/romfs_test.in +++ b/tests/romfs_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if ! which genromfs >/dev/null 2>&1; then echo "genromfs not installed; cannot test romfs." diff --git a/tests/serial_test.in b/tests/serial_test.in index 48655d4b9..331c3c3ad 100644 --- a/tests/serial_test.in +++ b/tests/serial_test.in @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with GRUB. If not, see . -set -e +set -ex grubshell=@builddir@/grub-shell . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/squashfs_test.in b/tests/squashfs_test.in index 2f044f95d..b973f7bac 100644 --- a/tests/squashfs_test.in +++ b/tests/squashfs_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if ! which mksquashfs >/dev/null 2>&1; then echo "mksquashfs not installed; cannot test squashfs." diff --git a/tests/syslinux_test.in b/tests/syslinux_test.in index 44d3cdf7a..e81c15310 100644 --- a/tests/syslinux_test.in +++ b/tests/syslinux_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex outfile="`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 99 diff --git a/tests/tar_test.in b/tests/tar_test.in index 6e2f2de8b..eafb6649d 100644 --- a/tests/tar_test.in +++ b/tests/tar_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if ! which tar >/dev/null 2>&1; then echo "tar not installed; cannot test tar." diff --git a/tests/test_sha512sum.in b/tests/test_sha512sum.in index b2bd89609..3866f40c8 100644 --- a/tests/test_sha512sum.in +++ b/tests/test_sha512sum.in @@ -1,5 +1,5 @@ #! @BUILD_SHEBANG@ -set -e +set -ex # create a randome file file="`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 99 diff --git a/tests/udf_test.in b/tests/udf_test.in index 8968fb103..b84d0b674 100644 --- a/tests/udf_test.in +++ b/tests/udf_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` diff --git a/tests/uhci_test.in b/tests/uhci_test.in index de199a281..46de98d92 100644 --- a/tests/uhci_test.in +++ b/tests/uhci_test.in @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with GRUB. If not, see . -set -e +set -ex grubshell=@builddir@/grub-shell . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/xfs_test.in b/tests/xfs_test.in index 8a648aa4a..d6b3ab908 100644 --- a/tests/xfs_test.in +++ b/tests/xfs_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` diff --git a/tests/xzcompress_test.in b/tests/xzcompress_test.in index cfc6ccba6..f0ac52e2d 100644 --- a/tests/xzcompress_test.in +++ b/tests/xzcompress_test.in @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with GRUB. If not, see . -set -e +set -ex grubshell=@builddir@/grub-shell . "@builddir@/grub-core/modinfo.sh" diff --git a/tests/zfs_test.in b/tests/zfs_test.in index c8bb7937f..e1cb766a5 100644 --- a/tests/zfs_test.in +++ b/tests/zfs_test.in @@ -1,6 +1,6 @@ #!@BUILD_SHEBANG@ -set -e +set -ex if [ "x$EUID" = "x" ] ; then EUID=`id -u` From 2bc0929a2fffbb60995605db6ce46aa3f979a7d2 Mon Sep 17 00:00:00 2001 From: Leo Sandoval Date: Fri, 19 Sep 2025 15:45:05 -0600 Subject: [PATCH 137/208] tests: Remove -w param from mkfs.hfsplus command Apparently the man page is outdated because the option "-w" is shown but not on "mkfs.hfsplus --usage". According to Gemini: The -w option is used to add an HFS wrapper around an HFS Plus file system, which is sometimes required for compatibility with older Mac OS 9 systems. However, this is not a standard or commonly used option and may not be available in all versions of the hfsprogs package, especially on Linux. Signed-off-by: Leo Sandoval Reviewed-by: Daniel Kiper --- tests/util/grub-fs-tester.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in index 126f19a7e..2302c2ab1 100644 --- a/tests/util/grub-fs-tester.in +++ b/tests/util/grub-fs-tester.in @@ -727,7 +727,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do "mkfs.hfsplus" -b $BLKSIZE -v "$FSLABEL" "${MOUNTDEVICE}" dd if=/dev/urandom of="${MOUNTDEVICE}" bs=1 seek=$((0x468)) conv=notrunc count=8 ;; x"hfsplus_wrap") - "mkfs.hfsplus" -w -b $BLKSIZE -v "$FSLABEL" "${MOUNTDEVICE}" + "mkfs.hfsplus" -b $BLKSIZE -v "$FSLABEL" "${MOUNTDEVICE}" dd if=/dev/urandom of="${MOUNTDEVICE}" bs=1 seek=$((0x468)) conv=notrunc count=8 MOUNTFS="hfsplus";; x"hfsplus_casesens") From 3a66437054e5bd94a02389db72d1f6ce40ca229a Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Thu, 6 Nov 2025 21:55:41 -0600 Subject: [PATCH 138/208] docs: Add security hardening suggestions Add some suggestions to the security section on maximizing the security hardening of GRUB. This change reveals sectioning issues introduced by commit 0b59d379f (docs/grub: Document signing GRUB under UEFI) and commit 0f2dda8cf (docs/grub: Document signing GRUB with an appended signature). Fix them on the occasion. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- docs/grub.texi | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index 5b23ae47b..cd4631aa3 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -9213,6 +9213,7 @@ environment variables and commands are listed in the same order. * TPM2 key protector:: Managing disk key with TPM2 key protector * Signing certificate and hash files:: Certificate and hash file signing * Signing GRUB itself:: Ensuring the integrity of the GRUB core image +* Hardening:: Configuration and customization to maximize security @end menu @node Authentication and authorisation @@ -10057,7 +10058,8 @@ loads GRUB to verify the integrity of the core image. This is ultimately platform-specific and individual platforms can define their own mechanisms. However, there are general-purpose mechanisms that can be used with GRUB. -@section Signing GRUB for UEFI secure boot + +@subsection Signing GRUB for UEFI secure boot On UEFI platforms, @file{core.img} is a PE binary. Therefore, it can be signed with a tool such as @command{pesign} or @command{sbsign}. Refer to the suggestions in @pxref{UEFI secure boot and shim} to ensure that the final @@ -10065,7 +10067,7 @@ image works under UEFI secure boot and can maintain the secure-boot chain. It will also be necessary to enroll the public key used into a relevant firmware key database. -@section Signing GRUB with an appended signature +@subsection Signing GRUB with an appended signature The @file{core.elf} itself can be signed with a Linux kernel module-style appended signature (@pxref{Using appended signatures}). To support IEEE1275 platforms where the boot image is often loaded directly @@ -10160,6 +10162,50 @@ dd if=core.elf.signed of=/dev/sda1 As with UEFI secure boot, it is necessary to build-in the required modules, or sign them if they are not part of the GRUB image. +@node Hardening +@section Hardening + +Security hardening involves additional / optional configuration and +customization steps to GRUB to maximize security. The extent to which +hardening can be accomplished depends on the threats attempting to be +mitigated for a given system / device, the device architecture, and number +of GRUB features required. The following is a listing of hardening steps which +may be considered: + +@itemize +@item (EFI Only) Enable secure boot to enable lockdown mode. This will limit +the attack surface of GRUB by limiting the commands and file systems +supported. (@pxref{Lockdown}) +@item (EFI Only) No-Execute capability of memory segments will be configured +by GRUB as indicated by the UEFI. This makes some classes of vulnerabilities +more difficult to exploit by providing support for marking memory as either +writable or executable. +@item (EFI Only) While building GRUB, the stack protector feature may be +enabled during the configuration step. This feature can make certain +vulnerabilities caused by stack buffer overflows more difficult to exploit. +This can be enabled by including the "--enable-stack-protector" flag to the +configure script: +@example +# @kbd{./configure --enable-stack-protector} +@end example +Please reference the file @file{INSTALL} for detailed instructions on how to +build GRUB. +@item Minimize the installed modules included with the GRUB installation. +For instance, if a specific file system is used for a given system, modules +for other file systems may be excluded. @pxref{Modules} for a list of +modules. +@item Minimize boot sources. In the GRUB configuration, reduce the possible +boot sources to the minimum needed for system operation. For instance, if +booting only from an internal drive, remove support for network booting +and booting from removable media. +@item Disable network support in GRUB if not required. Ensure network +interfaces are not configured in the GRUB configuration and consider +setting environment variable @samp{feature_net_search_cfg} to @samp{n} in an +embedded GRUB config file in order to disable attempting to use the +network for obtaining a GRUB config file. +@end itemize + + @node Platform limitations @chapter Platform limitations From f0170c21777ef42ec0d011da8a50ee970eae7cdd Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Mon, 10 Nov 2025 20:27:19 +0530 Subject: [PATCH 139/208] kern/ieee1275/openfw: Add missing grub_strdup() failure checks If grub_strdup() fails, it returns NULL and passing NULL further down to the code can lead to segmentation fault or an undefined behavior. Signed-off-by: Avnish Chouhan Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/kern/ieee1275/openfw.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/grub-core/kern/ieee1275/openfw.c b/grub-core/kern/ieee1275/openfw.c index 11b2beb2f..3b492dd96 100644 --- a/grub-core/kern/ieee1275/openfw.c +++ b/grub-core/kern/ieee1275/openfw.c @@ -201,6 +201,11 @@ grub_ieee1275_devalias_next (struct grub_ieee1275_devalias *alias) alias->path = 0; } tmp = grub_strdup (alias->name); + if (tmp == NULL) + { + grub_ieee1275_devalias_free (alias); + return 0; + } if (grub_ieee1275_next_property (alias->parent_dev, tmp, alias->name) <= 0) { @@ -432,9 +437,15 @@ grub_ieee1275_parse_args (const char *path, enum grub_ieee1275_parse_type ptype) ret = grub_strdup (args); else ret = grub_strndup (args, (grub_size_t)(comma - args)); - /* Consistently provide numbered partitions to GRUB. - OpenBOOT traditionally uses alphabetical partition - specifiers. */ + + if (ret == NULL) + return 0; + + /* + * Consistently provide numbered partitions to GRUB. + * OpenBOOT traditionally uses alphabetical partition + * specifiers. + */ if (ret[0] >= 'a' && ret[0] <= 'z') ret[0] = '1' + (ret[0] - 'a'); grub_free (args); From 9eebd67744a472a8b1885ecc8a0518fb1632ef12 Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Mon, 10 Nov 2025 20:27:20 +0530 Subject: [PATCH 140/208] script/execute: Add missing grub_strdup() failure check If grub_strdup() fails, it returns NULL and passing NULL further down to the code can lead to segmentation fault or an undefined behavior. Signed-off-by: Avnish Chouhan Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/script/execute.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c index da99dfa05..62afcdc99 100644 --- a/grub-core/script/execute.c +++ b/grub-core/script/execute.c @@ -1015,6 +1015,9 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd) { /* As a last resort, try if it is an assignment. */ char *assign = grub_strdup (cmdname); + if (assign == NULL) + return grub_errno; + char *eq = grub_strchr (assign, '='); if (eq) From f8c48cbedce59c02c359c9a35599d7568c8b98ce Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Mon, 10 Nov 2025 20:27:21 +0530 Subject: [PATCH 141/208] kern/mips/arc/init: Add missing grub_strdup() failure check If grub_strdup() fails, it returns NULL and passing NULL further down to the code can lead to segmentation fault or an undefined behavior. Signed-off-by: Avnish Chouhan Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/kern/mips/arc/init.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/kern/mips/arc/init.c b/grub-core/kern/mips/arc/init.c index 2ed3ff319..2b76988c7 100644 --- a/grub-core/kern/mips/arc/init.c +++ b/grub-core/kern/mips/arc/init.c @@ -403,6 +403,9 @@ grub_machine_get_bootlocation (char **device, char **path) if (!syspart) return; loaddev = grub_strdup (syspart); + if (loaddev == NULL) + return; + } partptr = get_part (loaddev); From 72b4c99376f7c6e6002dfbd7164194f346121871 Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Mon, 10 Nov 2025 20:27:22 +0530 Subject: [PATCH 142/208] osdep/linux/getroot: Add missing strdup() failure checks If strdup() fails, it returns NULL and passing NULL further down to the code can lead to segmentation fault or an undefined behavior. Signed-off-by: Avnish Chouhan Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/osdep/linux/getroot.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c index 38fe110fe..d71c373c3 100644 --- a/grub-core/osdep/linux/getroot.c +++ b/grub-core/osdep/linux/getroot.c @@ -991,6 +991,8 @@ grub_util_get_raid_grub_dev (const char *os_dev) char *p, *q; p = strdup (os_dev + sizeof ("/dev/md_d") - 1); + if (p == NULL) + return NULL; q = strchr (p, 'p'); if (q) @@ -1006,6 +1008,8 @@ grub_util_get_raid_grub_dev (const char *os_dev) char *p, *q; p = strdup (os_dev + sizeof ("/dev/md/d") - 1); + if (p == NULL) + return NULL; q = strchr (p, 'p'); if (q) @@ -1019,6 +1023,8 @@ grub_util_get_raid_grub_dev (const char *os_dev) char *p , *q; p = strdup (os_dev + sizeof ("/dev/md") - 1); + if (p == NULL) + return NULL; q = strchr (p, 'p'); if (q) @@ -1032,6 +1038,8 @@ grub_util_get_raid_grub_dev (const char *os_dev) char *p , *q; p = strdup (os_dev + sizeof ("/dev/md/") - 1); + if (p == NULL) + return NULL; q = strchr (p, 'p'); if (q) @@ -1046,6 +1054,8 @@ grub_util_get_raid_grub_dev (const char *os_dev) char *p , *q; p = strdup (os_dev + sizeof ("/dev/md/") - 1); + if (p == NULL) + return NULL; q = strchr (p, 'p'); if (q) From 73dee610b1b41034585321064e8158a246886f0b Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 17 Nov 2025 00:53:08 -0600 Subject: [PATCH 143/208] util/grub-mkrescue: Fix copy/paste issue referencing mdadm The check_xorriso() function appears to have been copy/pasted from somewhere that was originally checking the mdadm command. So the file handle to the output of the xorriso command is named "mdadm". Instead rename it to the more generic "fout". Also change a comment referencing mdadm to reference xorriso. Signed-off-by: Glenn Washburn Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- util/grub-mkrescue.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/util/grub-mkrescue.c b/util/grub-mkrescue.c index 6dc71a8a1..951b17b8e 100644 --- a/util/grub-mkrescue.c +++ b/util/grub-mkrescue.c @@ -339,7 +339,7 @@ check_xorriso (const char *val) const char *argv[5]; int fd; pid_t pid; - FILE *mdadm; + FILE *fout; char *buf = NULL; size_t len = 0; int ret = 0; @@ -356,12 +356,12 @@ check_xorriso (const char *val) if (!pid) return 0; - /* Parent. Read mdadm's output. */ - mdadm = fdopen (fd, "r"); - if (! mdadm) + /* Parent. Read xorriso's output. */ + fout = fdopen (fd, "r"); + if (! fout) return 0; - while (getline (&buf, &len, mdadm) > 0) + while (getline (&buf, &len, fout) > 0) { if (grub_strstr (buf, val)) ret = 1; From ea0b76dc4a003f2b7f828ef0c24cc8227148b2f8 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Thu, 6 Nov 2025 18:24:33 -0600 Subject: [PATCH 144/208] util/grub.d/00_header.in: Disable loading all_video for EFI Loading all_video for EFI can cause video issues in some cases since GRUB Bochs/Cirrus drivers may conflict with native EFI drivers. Change default behavior for EFI to only load EFI specific video modules. Also include a new environment variable to restore the old behavior if needed. Fixes: https://savannah.gnu.org/bugs/?66200 Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- docs/grub.texi | 7 +++++++ util/grub-mkconfig.in | 3 ++- util/grub.d/00_header.in | 16 +++++++++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index cd4631aa3..d4287181c 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -1642,6 +1642,13 @@ This option is unset by default, and is deprecated in favour of the less confusing @samp{GRUB_TIMEOUT_STYLE=countdown} or @samp{GRUB_TIMEOUT_STYLE=hidden}. +@item GRUB_FORCE_EFI_ALL_VIDEO +When set to true, this will allow grub-mkconfig to generate a GRUB config +that supports loading the all_video module on the EFI platform instead of +just the efi_gop and efi_uga modules. + +This option is unset by default. + @end table For more detailed customisation of @command{grub-mkconfig}'s output, you may diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index 32c480dae..45787a1d1 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -255,7 +255,8 @@ export GRUB_DEFAULT \ GRUB_ENABLE_CRYPTODISK \ GRUB_BADRAM \ GRUB_OS_PROBER_SKIP_LIST \ - GRUB_DISABLE_SUBMENU + GRUB_DISABLE_SUBMENU \ + GRUB_FORCE_EFI_ALL_VIDEO if test "x${grub_cfg}" != "x"; then rm -f "${grub_cfg}.new" diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in index 9d36feda3..77834cfaf 100644 --- a/util/grub.d/00_header.in +++ b/util/grub.d/00_header.in @@ -124,10 +124,24 @@ if [ -n "${GRUB_VIDEO_BACKEND}" ]; then insmod ${GRUB_VIDEO_BACKEND} EOF else +# For EFI, use EFI video drivers only by default to avoid conflict between +# GRUB Bochs/Cirrus and native EFI drivers. If GRUB_FORCE_EFI_ALL_VIDEO is +# set/true then defer back to all_video even for EFI. +if [ "${GRUB_FORCE_EFI_ALL_VIDEO}" = "1" ]; then + cat < Date: Fri, 14 Nov 2025 17:40:08 -0600 Subject: [PATCH 145/208] tests: Split ZFS ZSTD test into new file Split ZFS ZSTD test into its own test script. Add a check to the new test script to see if the zfs utility installed on the host supports "zstd" compression before running the test and fail the test if not. It seems at least some zfs-fuse binaries do not support zstd compression and the current test will fail in that case. Splitting into a new file will avoid masking other test failures due to missing zstd support. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper Reviewed-by: Glenn Washburn --- .gitignore | 1 + Makefile.util.def | 6 ++++++ tests/zfs_test.in | 1 - tests/zfs_zstd_test.in | 30 ++++++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 tests/zfs_zstd_test.in diff --git a/.gitignore b/.gitignore index 524f2e6d0..67ad2d26d 100644 --- a/.gitignore +++ b/.gitignore @@ -283,3 +283,4 @@ widthspec.bin /xfs_test /xzcompress_test /zfs_test +/zfs_zstd_test diff --git a/Makefile.util.def b/Makefile.util.def index 91720e627..7b91c0b61 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -914,6 +914,12 @@ script = { common = tests/zfs_test.in; }; +script = { + testcase = native; + name = zfs_zstd_test; + common = tests/zfs_zstd_test.in; +}; + script = { testcase = native; name = cpio_test; diff --git a/tests/zfs_test.in b/tests/zfs_test.in index e1cb766a5..cd547b4d2 100644 --- a/tests/zfs_test.in +++ b/tests/zfs_test.in @@ -19,7 +19,6 @@ fi "@builddir@/grub-fs-tester" zfs_lzjb "@builddir@/grub-fs-tester" zfs_gzip "@builddir@/grub-fs-tester" zfs_zle -"@builddir@/grub-fs-tester" zfs_zstd "@builddir@/grub-fs-tester" zfs_raidz3 "@builddir@/grub-fs-tester" zfs_raidz2 "@builddir@/grub-fs-tester" zfs_raidz diff --git a/tests/zfs_zstd_test.in b/tests/zfs_zstd_test.in new file mode 100644 index 000000000..8cafceaaf --- /dev/null +++ b/tests/zfs_zstd_test.in @@ -0,0 +1,30 @@ +#!@BUILD_SHEBANG@ + +set -e + +if [ "x$EUID" = "x" ] ; then + EUID=`id -u` +fi + +if [ "$EUID" != 0 ] ; then + exit 99 +fi + +if ! which zpool >/dev/null 2>&1; then + echo "zpool not installed; cannot test zfs." + exit 99 +fi + +if ! which zfs >/dev/null 2>&1; then + echo "zfs not installed; cannot test zfs." + exit 99 +fi + +# If ZFS ZSTD compression is not supported (as is the case with zfs-fuse +# for example at the time of writing) then fail early the ZSTD compression testing. +if ! zfs get 2>&1 | grep -q "compression.*zstd"; then + echo "zfs zstd compression not supported; cannot test zfs zstd." + exit 99 +fi + +"@builddir@/grub-fs-tester" zfs_zstd From a377cd67d0519b84ce8a01dfc261924890329946 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Fri, 14 Nov 2025 17:40:09 -0600 Subject: [PATCH 146/208] tests: Avoid test failure in erofs for label length Recently, mkfs.erofs began to enforce that the file system label is 15 characters or less (excluding NUL terminator). This causes the current erofs test in GRUB to fail. Reduce the test label used to fit in this limit allowing the test to work as expected. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper Reviewed-by: Glenn Washburn --- tests/util/grub-fs-tester.in | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in index 2302c2ab1..54add3b73 100644 --- a/tests/util/grub-fs-tester.in +++ b/tests/util/grub-fs-tester.in @@ -386,9 +386,12 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do FSLABEL="g;/_é䏌䐓䏕䎛䎾䏴кит u" #FSLABEL="g;/_é莭莽😁кит u" ;; - # FS LIMITATION: reiserfs, extN, jfs and erofs label is at most 16 UTF-8 characters - x"reiserfs_old" | x"reiserfs" | x"ext"* | x"lvm"* | x"luks"* | x"mdraid"* | x"jfs" | x"jfs_caseins" | x"erofs_"*) + # FS LIMITATION: reiserfs, extN, and jfs label is at most 16 UTF-8 characters + x"reiserfs_old" | x"reiserfs" | x"ext"* | x"lvm"* | x"luks"* | x"mdraid"* | x"jfs" | x"jfs_caseins") FSLABEL="g;/éт 莭😁";; + # FS LIMITATION: erofs label is at most 15 UTF-8 characters + x"erofs_"*) + FSLABEL="g;/é 莭😁";; # FS LIMITATION: No underscore, space, semicolon, slash or international characters in UFS* in label. Limited to 32 UTF-8 characters x"ufs1" | x"ufs1_sun" | x"ufs2") FSLABEL="grubtest""ieurrucnenreeiurueurewf";; From e4e99a8f84d4481f977306ec510865e4783db258 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Fri, 14 Nov 2025 17:40:10 -0600 Subject: [PATCH 147/208] tests: Support changed mkfs.ext2 behavior for -r flag Correct nuisance ext234_test failure on newer Linux distros. Recently, the mkfs.ext2 utility removed support for the -r flag to specify old (version 0) formats of ext2. A new flag was added to allow the same behavior. Support both ways of specifying version 0 ext2 file systems when testing ext2 in GRUB. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper Reviewed-by: Glenn Washburn --- tests/util/grub-fs-tester.in | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in index 54add3b73..9a13f87a3 100644 --- a/tests/util/grub-fs-tester.in +++ b/tests/util/grub-fs-tester.in @@ -880,7 +880,11 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do xnilfs2) "mkfs.nilfs2" -L "$FSLABEL" -b $BLKSIZE -q "${MOUNTDEVICE}" ;; xext2_old) - MKE2FS_DEVICE_SECTSIZE=$SECSIZE "mkfs.ext2" -r 0 -b $BLKSIZE -L "$FSLABEL" -q "${MOUNTDEVICE}" + if "mkfs.ext2" -r 0 2>&1 | grep -q -F "the -r option has been removed"; then + MKE2FS_DEVICE_SECTSIZE=$SECSIZE "mkfs.ext2" -E revision=0 -b $BLKSIZE -L "$FSLABEL" -q "${MOUNTDEVICE}" + else + MKE2FS_DEVICE_SECTSIZE=$SECSIZE "mkfs.ext2" -r 0 -b $BLKSIZE -L "$FSLABEL" -q "${MOUNTDEVICE}" + fi MOUNTFS=ext2 ;; xext4_metabg) From 0879ff7bc6e6b31126953d8a45f8090ea817c6e8 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Wed, 12 Nov 2025 21:10:39 -0600 Subject: [PATCH 148/208] tests/erofs_test: Fix mkfs.erofs version test to not use process substitution bashism The shell used to run the tests is generally /bin/sh, which does not support process substitution. Fixes: b990df0bef9e (tests/util/grub-fs-tester: Fix EROFS label tests in grub-fs-tester) Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper Reviewed-by: Sudhakar Kuppusamy --- tests/util/grub-fs-tester.in | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in index 9a13f87a3..f8181837e 100644 --- a/tests/util/grub-fs-tester.in +++ b/tests/util/grub-fs-tester.in @@ -599,11 +599,12 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do | x"minix3" | xreiserfs_old) NOFSLABEL=y;; x"erofs_"*) - MKFS_EROFS_VERSION=$(mkfs.erofs -V 2>/dev/null | tr ' ' '\n' | grep '^[0-9]') + MKFS_EROFS_VERSION=$(mkfs.erofs 2>/dev/null | head -n 1 | (read _ V; echo $V)) # check if the version is at least 1.6 - if [ $(sort -V <(echo "$MKFS_EROFS_VERSION") <(echo "1.6") | head -n 1) != "1.6" ]; then + if [ "$(echo -e "${MKFS_EROFS_VERSION}\n1.6" | sort -V | head -n 1)" != "1.6" ]; then NOFSLABEL=y - fi + unset FSLABEL + fi;; esac PDIRCOMPNUM=210 From b6aceba7fb69bce4967a6eb0b3cd627936089003 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Wed, 12 Nov 2025 21:10:40 -0600 Subject: [PATCH 149/208] tests/erofs_test: Remove root check The erofs tests do not ever mount the generated erofs image. So root is not needed, as with the squashfs and iso9660 filesystems. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper Reviewed-by: Sudhakar Kuppusamy --- tests/erofs_test.in | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/erofs_test.in b/tests/erofs_test.in index e3f3ea5eb..f8cf3bb8b 100644 --- a/tests/erofs_test.in +++ b/tests/erofs_test.in @@ -2,14 +2,6 @@ set -ex -if [ "x$EUID" = "x" ] ; then - EUID=`id -u` -fi - -if [ "$EUID" != 0 ] ; then - exit 99 -fi - if ! which mkfs.erofs >/dev/null 2>&1; then echo "mkfs.erofs not installed; cannot test erofs." exit 77 From a94d3ab7fc75cfc473efa203e235c817d99875b2 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Wed, 12 Nov 2025 21:10:41 -0600 Subject: [PATCH 150/208] tests/erofs_test: Disable filetime check for erofs_compact Compact EROFS inodes do not allow for modification times that are different from FS creation times. The file modification time check is done between the EROFS image and the file system where test temporary files are written to, not the files as seen from the mounted EROFS image. So its likely that the file modification time will be different, more so when run on slower systems. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper Reviewed-by: Sudhakar Kuppusamy --- tests/util/grub-fs-tester.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in index f8181837e..5b15007e5 100644 --- a/tests/util/grub-fs-tester.in +++ b/tests/util/grub-fs-tester.in @@ -590,6 +590,10 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do # FS LIMITATION: romfs has no timestamps. x"romfs") NOFILETIME=y; NOFSTIME=y;; + # FS LIMITATION: Compact inodes do not allow for modification + # times that are different from FS creation times. + x"erofs_compact") + NOFILETIME=y;; esac NOFSLABEL=n From de8961314f8c1685fb5c6bd75eb668fa5416c7ce Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Wed, 12 Nov 2025 21:10:42 -0600 Subject: [PATCH 151/208] tests/erofs_test: Improve accuracy of FSTIME check On resource constrained test runs, the last modification time on the image is an unreliable date to check against the filesystem creation time. Use dump.erofs to get the filesystem creation time from the superblock. This should get the timestamp as shown by GRUB's "ls -l". Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper Reviewed-by: Sudhakar Kuppusamy --- tests/util/grub-fs-tester.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in index 5b15007e5..bfb7b0553 100644 --- a/tests/util/grub-fs-tester.in +++ b/tests/util/grub-fs-tester.in @@ -1502,6 +1502,12 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do # With some abstractions like mdraid flushing to disk # may be delayed for a long time. FSTIME="$UMOUNT_TIME";; + x"erofs_"*) + # Creating the erofs image may take more than a few + # seconds. Use the more accurate timestamp from the + # superblock. + FSTIME="$(dump.erofs -s "${FSIMAGEP}0.img" | grep ^"Filesystem created:" | (read _ _ REST; echo $REST) )" + FSTIME="$(date -d "$FSTIME" -u '+%Y-%m-%d %H:%M:%S')";; xsquash*) # Creating the squash image may take more than a few # seconds. Use the more accurate timestamp from the From 6ae8fd0ee5db0c1e4d243b9c5e2a6c6063ca74d1 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 17 Nov 2025 00:40:25 -0600 Subject: [PATCH 152/208] tests/grub_cmd_cryptomount: Expect test success, now that GRUB supports Argon2 This test was included to verify that, while GRUB did not have Argon2 support, trying to open a LUKS2 volume with an Argon2 keyslot would fail Now that Argon2 support is included, the test is failing because it expected a failure, but is now getting success. Change the test to expect success. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper Reviewed-by: Sudhakar Kuppusamy --- tests/grub_cmd_cryptomount.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/grub_cmd_cryptomount.in b/tests/grub_cmd_cryptomount.in index 25d7d8c36..bf991818f 100644 --- a/tests/grub_cmd_cryptomount.in +++ b/tests/grub_cmd_cryptomount.in @@ -178,8 +178,7 @@ eval testcase "'LUKS2 test with different metadata size:'" \ @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \ "--cs-opts='--luks2-metadata-size 512k'" -# TODO: Expect a failure with LUKS2 volumes with argon2 key derivation -eval testcase_fail "'LUKS2 test with argon2 pbkdf:'" \ +eval testcase "'LUKS2 test with argon2 pbkdf:'" \ @builddir@/grub-shell-luks-tester --luks=2 $COMMON_OPTS \ "--cs-opts='--pbkdf-memory 32'" "--cs-opts='--pbkdf-parallel 1'" From 445549c8989b4202ecd04e9f487aef996857802b Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 17 Nov 2025 00:40:26 -0600 Subject: [PATCH 153/208] tests/grub_cmd_cryptomount: Use builddir shell variable and quote Using a shell variable instead of an autoconf macro creates less changes to the file, which can be desirable when modifying by hand later and then diffing with the unexpanded file. It also makes it simpler to change the builddir after expansion, which may need to happen when moving the build dir to a different path and not being able to rerun the autoconf expansion. Also, add quoting around the builddir variable as there may be spaces in the path. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper Reviewed-by: Sudhakar Kuppusamy --- tests/grub_cmd_cryptomount.in | 47 ++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/tests/grub_cmd_cryptomount.in b/tests/grub_cmd_cryptomount.in index bf991818f..97863c3e8 100644 --- a/tests/grub_cmd_cryptomount.in +++ b/tests/grub_cmd_cryptomount.in @@ -38,6 +38,7 @@ fi COMMON_OPTS='${V:+--debug=$V} --cs-opts="--pbkdf-force-iterations 1000"' debug=${GRUB_SHELL_DEFAULT_DEBUG:-$GRUB_TEST_DEFAULT_DEBUG} +builddir="@builddir@" _testcase() { local EXPECTEDRES=$1 @@ -92,94 +93,94 @@ testcase_fail() { _testcase 1 "$@"; } ### LUKS1 tests eval testcase "'LUKS1 test cryptsetup defaults:'" \ - @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS + "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS eval testcase "'LUKS1 test with twofish cipher:'" \ - @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS \ "--cs-opts='--cipher twofish-xts-plain64'" eval testcase "'LUKS1 test key file support:'" \ - @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS \ --keyfile eval testcase "'LUKS1 test key file with offset:'" \ - @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS \ --keyfile --cs-opts="--keyfile-offset=237" eval testcase "'LUKS1 test key file with offset and size:'" \ - @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS \ --keyfile "--cs-opts='--keyfile-offset=237 --keyfile-size=1023'" eval testcase "'LUKS1 test detached header support:'" \ - @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS \ --detached-header eval testcase "'LUKS1 test both detached header and key file:'" \ - @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS \ --keyfile --detached-header ### LUKS2 tests (mirroring the LUKS1 tests above) LUKS2_COMMON_OPTS="--luks=2 --cs-opts=--pbkdf=pbkdf2" eval testcase "'LUKS2 test cryptsetup defaults:'" \ - @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS + "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS eval testcase "'LUKS2 test with twofish cipher:'" \ - @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \ "--cs-opts='--cipher twofish-xts-plain64'" eval testcase "'LUKS2 test key file support:'" \ - @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \ --keyfile eval testcase "'LUKS2 test key file with offset:'" \ - @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \ --keyfile --cs-opts="--keyfile-offset=237" eval testcase "'LUKS2 test key file with offset and size:'" \ - @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \ --keyfile "--cs-opts='--keyfile-offset=237 --keyfile-size=1023'" eval testcase "'LUKS2 test detached header support:'" \ - @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \ --detached-header eval testcase "'LUKS2 test both detached header and key file:'" \ - @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \ --keyfile --detached-header ### LUKS1 specific tests # Tests for xts-plain and xts-plain64 modes eval testcase "'LUKS1 test cryptsetup xts-plain:'" \ - @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS \ "--cs-opts='--cipher aes-xts-plain'" eval testcase "'LUKS1 test cryptsetup xts-plain64:'" \ - @builddir@/grub-shell-luks-tester --luks=1 $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" --luks=1 $COMMON_OPTS \ "--cs-opts='--cipher aes-xts-plain64'" ### LUKS2 specific tests eval testcase "'LUKS2 test with 1k sector size:'" \ - @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \ "--cs-opts='--sector-size 1024'" eval testcase "'LUKS2 test with 2k sector size:'" \ - @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \ "--cs-opts='--sector-size 2048'" eval testcase "'LUKS2 test with 4k sector size:'" \ - @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \ "--cs-opts='--sector-size 4096'" eval testcase "'LUKS2 test with non-default key slot:'" \ - @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \ "--cs-opts='--key-slot 5'" eval testcase "'LUKS2 test with different metadata size:'" \ - @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \ "--cs-opts='--luks2-metadata-size 512k'" eval testcase "'LUKS2 test with argon2 pbkdf:'" \ - @builddir@/grub-shell-luks-tester --luks=2 $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" --luks=2 $COMMON_OPTS \ "--cs-opts='--pbkdf-memory 32'" "--cs-opts='--pbkdf-parallel 1'" # Add good password to second slot and change first slot to unchecked password @@ -191,7 +192,7 @@ cat >$csscript <<'EOF' EOF eval testcase "'LUKS2 test with second key slot and first slot using different password:'" \ - @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \ + "$builddir/grub-shell-luks-tester" $LUKS2_COMMON_OPTS $COMMON_OPTS \ "--cs-script='$csscript'" test -n "$debug" || rm "$csscript" From 8b5d3bdf876ac661a6448c634743c6255035975a Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Tue, 11 Nov 2025 01:15:53 -0600 Subject: [PATCH 154/208] gitignore: Remove po/*.po and po/LINGUAS po files will now be tracked. This has the following benefits as listed by Colin Watson: * Build processes would no longer be vulnerable to an external server potentially going down for an extended period of time; they'd be stuck with outdated translations until the server was fixed or came up with a workaround, but that's better than nothing. * It would be easier to manage branches of stable releases, rather than assuming that translations downloaded for master will match the POT files for a stable release. * Tests would be able to pass from a clean git checkout without relying on an external server, improving QA reliability. * It would be easier to make and test branches while offline. * The translations shipped with a release tarball could be tagged in git so that it's easy to investigate bugs in them. * Downstream distributors would be able to use git branches without having to fill in additional files. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 67ad2d26d..db16295ad 100644 --- a/.gitignore +++ b/.gitignore @@ -249,8 +249,6 @@ widthspec.bin /pata_test /po/*.gmo /po/*.mo -/po/*.po -/po/LINGUAS /po/Makefile.in.in /po/Makevars /po/Makevars.template From f7bcca3168aeb25e45c8d81afc03081f5694b73e Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Sun, 16 Nov 2025 22:23:41 -0600 Subject: [PATCH 155/208] linguas: Ensure that linguas.sh runs from the directory that it resides The script assumes that it is run from the root of the source tree, which is where it is located. So this should be enforced to prevent accidental misuses. realpath is used instead of readlink as that is recommended in Debian's readlink manpage since at least Debian 11. Also, use the shell variable's parameter expansion for removing a suffix pattern to get the directory in which the script resides. This is preferable to using the dirname binary as it avoids creating a new process. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- linguas.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/linguas.sh b/linguas.sh index b95ad4f7d..45cdf2ac1 100755 --- a/linguas.sh +++ b/linguas.sh @@ -1,5 +1,10 @@ #!/bin/sh +SDIR=$(realpath -e "$0") +SDIR=${SDIR%/*} + +cd "$SDIR" + rsync -Lrtvz translationproject.org::tp/latest/grub/ po autogenerated="en@quot en@hebrew de@hebrew en@cyrillic en@greek en@arabic en@piglatin de_CH" From 2464d43829588909cc6c69027c7d965000e632c1 Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Mon, 17 Nov 2025 07:11:14 +0000 Subject: [PATCH 156/208] mmap/mmap: Fix resource leak In the function grub_mmap_iterate(), memory is allocated to "ctx.scanline_events" and "present" but isn't freed when error handling grub_malloc(). Prior to returning grub_errno, these variables should be freed to prevent a resource leak. Fixes: CID 96655 Signed-off-by: Alec Brown Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/mmap/mmap.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/grub-core/mmap/mmap.c b/grub-core/mmap/mmap.c index 8f03b7765..7c7d3911c 100644 --- a/grub-core/mmap/mmap.c +++ b/grub-core/mmap/mmap.c @@ -242,8 +242,12 @@ grub_mmap_iterate (grub_memory_hook_t hook, void *hook_data) else { struct mm_list *n = grub_malloc (sizeof (*n)); - if (n == NULL) - return grub_errno; + if (n == NULL) + { + grub_free (ctx.scanline_events); + grub_free (present); + return grub_errno; + } n->val = ctx.scanline_events[i].memtype; n->present = 1; From 95e614a11b6bd3f5bf0df5157fed588fef1ae9cb Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Mon, 17 Nov 2025 07:11:15 +0000 Subject: [PATCH 157/208] fs/zfs/zfs: Avoid pointer downcasting in dnode_get() Coverity marks multiple issues in grub-core/fs/zfs/zfs.c as either "Untrusted value as argument", "Untrusted pointer read", or "Untrusted loop bound". Each of these issues share a common cause where Coverity finds that data->dnode_buf gets tainted by dnbuf since it is downcasting from (void *) to (dnode_phys_t *) and could imply that the data the pointer points to is tainted. However, the function zio_read(), which reads this data from disk, sanitizes this data by verifying its checksum. To resolve the issues for Coverity, setting dnbuf to (dnode_phys_t *) at the start of the function dnode_get() seems to do the trick. Fixes: CID 314020 Fixes: CID 896330 Fixes: CID 896331 Fixes: CID 896334 Fixes: CID 896336 Fixes: CID 896340 Fixes: CID 897337 Signed-off-by: Alec Brown Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/fs/zfs/zfs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index afe821f9b..83dfa6d52 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -2743,7 +2743,7 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type, grub_uint64_t blkid, blksz; /* the block id this object dnode is in */ int epbs; /* shift of number of dnodes in a block */ int idx; /* index within a block */ - void *dnbuf; + dnode_phys_t *dnbuf; grub_err_t err; grub_zfs_endian_t endian; @@ -2773,7 +2773,7 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type, grub_dprintf ("zfs", "endian = %d, blkid=%llx\n", mdn->endian, (unsigned long long) blkid); - err = dmu_read (mdn, blkid, &dnbuf, &endian, data); + err = dmu_read (mdn, blkid, (void **) &dnbuf, &endian, data); if (err) return err; grub_dprintf ("zfs", "alive\n"); @@ -2795,7 +2795,7 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type, data->dnode_endian = endian; } - grub_memmove (&(buf->dn), (dnode_phys_t *) dnbuf + idx, DNODE_SIZE); + grub_memmove (&(buf->dn), dnbuf + idx, DNODE_SIZE); if (data->dnode_buf == 0) /* dnbuf not used anymore if data->dnode_mdn malloc failed */ grub_free (dnbuf); From cc9d621dd06bfa12eac511b37b4ceda5bd2f8246 Mon Sep 17 00:00:00 2001 From: Thomas Frauendorfer | Miray Software Date: Fri, 9 May 2025 13:51:08 +0200 Subject: [PATCH 158/208] commands/test: Fix error in recursion depth calculation The commit c68b7d236 (commands/test: Stack overflow due to unlimited recursion depth) added recursion depth tests to the test command. But in the error case it decrements the pointer to the depth value instead of the value itself. Fix it. Fixes: c68b7d236 (commands/test: Stack overflow due to unlimited recursion depth) Signed-off-by: Thomas Frauendorfer | Miray Software Reviewed-by: Daniel Kiper --- grub-core/commands/test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/commands/test.c b/grub-core/commands/test.c index b585c3d70..ee47ab264 100644 --- a/grub-core/commands/test.c +++ b/grub-core/commands/test.c @@ -403,7 +403,7 @@ test_parse (char **args, int *argn, int argc, int *depth) if (++(*depth) > MAX_TEST_RECURSION_DEPTH) { grub_error (GRUB_ERR_OUT_OF_RANGE, N_("max recursion depth exceeded")); - depth--; + (*depth)--; return ctx.or || ctx.and; } From c4fb4cbc941981894a00ba8e75d634a41967a27f Mon Sep 17 00:00:00 2001 From: Thomas Frauendorfer | Miray Software Date: Wed, 7 May 2025 16:15:22 +0200 Subject: [PATCH 159/208] kern/file: Call grub_dl_unref() after fs->fs_close() With commit 16f196874 (kern/file: Implement filesystem reference counting) files hold a reference to their file systems. When closing a file in grub_file_close() we should not expect file->fs to stay valid after calling grub_dl_unref() on file->fs->mod. So, grub_dl_unref() should be called after file->fs->fs_close(). Fixes: CVE-2025-54771 Fixes: 16f196874 (kern/file: Implement filesystem reference counting) Reported-by: Thomas Frauendorfer | Miray Software Signed-off-by: Thomas Frauendorfer | Miray Software Reviewed-by: Daniel Kiper --- grub-core/kern/file.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c index 6e7efe89a..eb52fd25f 100644 --- a/grub-core/kern/file.c +++ b/grub-core/kern/file.c @@ -201,12 +201,12 @@ grub_file_read (grub_file_t file, void *buf, grub_size_t len) grub_err_t grub_file_close (grub_file_t file) { - if (file->fs->mod) - grub_dl_unref (file->fs->mod); - if (file->fs->fs_close) (file->fs->fs_close) (file); + if (file->fs->mod) + grub_dl_unref (file->fs->mod); + if (file->device) grub_device_close (file->device); grub_free (file->name); From 10e58a14db20e17d1b6a39abe38df01fef98e29d Mon Sep 17 00:00:00 2001 From: Thomas Frauendorfer | Miray Software Date: Fri, 9 May 2025 14:20:47 +0200 Subject: [PATCH 160/208] net/net: Unregister net_set_vlan command on unload The commit 954c48b9c (net/net: Add net_set_vlan command) added command net_set_vlan to the net module. Unfortunately the commit only added the grub_register_command() call on module load but missed the grub_unregister_command() on unload. Let's fix this. Fixes: CVE-2025-54770 Fixes: 954c48b9c (net/net: Add net_set_vlan command) Reported-by: Thomas Frauendorfer | Miray Software Signed-off-by: Thomas Frauendorfer | Miray Software Reviewed-by: Daniel Kiper --- grub-core/net/net.c | 1 + 1 file changed, 1 insertion(+) diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 6ea33d1cd..6c94a3b1e 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -2146,6 +2146,7 @@ GRUB_MOD_FINI(net) grub_unregister_command (cmd_deladdr); grub_unregister_command (cmd_addroute); grub_unregister_command (cmd_delroute); + grub_unregister_command (cmd_setvlan); grub_unregister_command (cmd_lsroutes); grub_unregister_command (cmd_lscards); grub_unregister_command (cmd_lsaddr); From 8ed78fd9f0852ab218cc1f991c38e5a229e43807 Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Thu, 21 Aug 2025 21:14:06 +0000 Subject: [PATCH 161/208] gettext/gettext: Unregister gettext command on module unload When the gettext module is loaded, the gettext command is registered but isn't unregistered when the module is unloaded. We need to add a call to grub_unregister_command() when unloading the module. Fixes: CVE-2025-61662 Reported-by: Alec Brown Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/gettext/gettext.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/grub-core/gettext/gettext.c b/grub-core/gettext/gettext.c index 9ffc73428..edebed998 100644 --- a/grub-core/gettext/gettext.c +++ b/grub-core/gettext/gettext.c @@ -502,6 +502,8 @@ grub_cmd_translate (grub_command_t cmd __attribute__ ((unused)), return 0; } +static grub_command_t cmd; + GRUB_MOD_INIT (gettext) { const char *lang; @@ -521,13 +523,14 @@ GRUB_MOD_INIT (gettext) grub_register_variable_hook ("locale_dir", NULL, read_main); grub_register_variable_hook ("secondary_locale_dir", NULL, read_secondary); - grub_register_command_p1 ("gettext", grub_cmd_translate, - N_("STRING"), - /* TRANSLATORS: It refers to passing the string through gettext. - So it's "translate" in the same meaning as in what you're - doing now. - */ - N_("Translates the string with the current settings.")); + cmd = grub_register_command_p1 ("gettext", grub_cmd_translate, + N_("STRING"), + /* + * TRANSLATORS: It refers to passing the string through gettext. + * So it's "translate" in the same meaning as in what you're + * doing now. + */ + N_("Translates the string with the current settings.")); /* Reload .mo file information if lang changes. */ grub_register_variable_hook ("lang", NULL, grub_gettext_env_write_lang); @@ -544,6 +547,8 @@ GRUB_MOD_FINI (gettext) grub_register_variable_hook ("secondary_locale_dir", NULL, NULL); grub_register_variable_hook ("lang", NULL, NULL); + grub_unregister_command (cmd); + grub_gettext_delete_list (&main_context); grub_gettext_delete_list (&secondary_context); From 05d3698b8b03eccc49e53491bbd75dba15f40917 Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Thu, 21 Aug 2025 21:14:07 +0000 Subject: [PATCH 162/208] normal/main: Unregister commands on module unload When the normal module is loaded, the normal and normal_exit commands are registered but aren't unregistered when the module is unloaded. We need to add calls to grub_unregister_command() when unloading the module for these commands. Fixes: CVE-2025-61663 Fixes: CVE-2025-61664 Reported-by: Alec Brown Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/normal/main.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 8c2acf938..de9a3f961 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -510,7 +510,7 @@ grub_mini_cmd_clear (struct grub_command *cmd __attribute__ ((unused)), return 0; } -static grub_command_t cmd_clear; +static grub_command_t cmd_clear, cmd_normal, cmd_normal_exit; static void (*grub_xputs_saved) (const char *str); static const char *features[] = { @@ -554,10 +554,10 @@ GRUB_MOD_INIT(normal) grub_env_export ("pager"); /* Register a command "normal" for the rescue mode. */ - grub_register_command ("normal", grub_cmd_normal, - 0, N_("Enter normal mode.")); - grub_register_command ("normal_exit", grub_cmd_normal_exit, - 0, N_("Exit from normal mode.")); + cmd_normal = grub_register_command ("normal", grub_cmd_normal, + 0, N_("Enter normal mode.")); + cmd_normal_exit = grub_register_command ("normal_exit", grub_cmd_normal_exit, + 0, N_("Exit from normal mode.")); /* Reload terminal colors when these variables are written to. */ grub_register_variable_hook ("color_normal", NULL, grub_env_write_color_normal); @@ -599,4 +599,6 @@ GRUB_MOD_FINI(normal) grub_register_variable_hook ("color_highlight", NULL, NULL); grub_fs_autoload_hook = 0; grub_unregister_command (cmd_clear); + grub_unregister_command (cmd_normal); + grub_unregister_command (cmd_normal_exit); } From 9df1e693e70c5a274b6d60dc76efe2694b89c2fc Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Thu, 21 Aug 2025 21:14:08 +0000 Subject: [PATCH 163/208] tests/lib/functional_test: Unregister commands on module unload When the functional_test module is loaded, both the functional_test and all_functional_test commands are registered but only the all_functional_test command is being unregistered since it was the last to set the cmd variable that gets unregistered when the module is unloaded. To unregister both commands, we need to create an additional grub_extcmd_t variable. Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/tests/lib/functional_test.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/grub-core/tests/lib/functional_test.c b/grub-core/tests/lib/functional_test.c index 94a606bf9..776934e1f 100644 --- a/grub-core/tests/lib/functional_test.c +++ b/grub-core/tests/lib/functional_test.c @@ -92,17 +92,18 @@ grub_functional_all_tests (grub_extcmd_context_t ctxt __attribute__ ((unused)), return GRUB_ERR_NONE; } -static grub_extcmd_t cmd; +static grub_extcmd_t cmd, cmd_all; GRUB_MOD_INIT (functional_test) { cmd = grub_register_extcmd ("functional_test", grub_functional_test, 0, 0, "Run all loaded functional tests.", 0); - cmd = grub_register_extcmd ("all_functional_test", grub_functional_all_tests, 0, 0, - "Run all functional tests.", 0); + cmd_all = grub_register_extcmd ("all_functional_test", grub_functional_all_tests, 0, 0, + "Run all functional tests.", 0); } GRUB_MOD_FINI (functional_test) { grub_unregister_extcmd (cmd); + grub_unregister_extcmd (cmd_all); } From 549a9cc372fd0b96a4ccdfad0e12140476cc62a3 Mon Sep 17 00:00:00 2001 From: Jamie Date: Mon, 14 Jul 2025 09:52:59 +0100 Subject: [PATCH 164/208] commands/usbtest: Use correct string length field An incorrect length field is used for buffer allocation. This leads to grub_utf16_to_utf8() receiving an incorrect/different length and possibly causing OOB write. This makes sure to use the correct length. Fixes: CVE-2025-61661 Reported-by: Jamie Signed-off-by: Jamie Reviewed-by: Daniel Kiper --- grub-core/commands/usbtest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/commands/usbtest.c b/grub-core/commands/usbtest.c index 2c6d93fe6..8ef187a9a 100644 --- a/grub-core/commands/usbtest.c +++ b/grub-core/commands/usbtest.c @@ -99,7 +99,7 @@ grub_usb_get_string (grub_usb_device_t dev, grub_uint8_t index, int langid, return GRUB_USB_ERR_NONE; } - *string = grub_malloc (descstr.length * 2 + 1); + *string = grub_malloc (descstrp->length * 2 + 1); if (! *string) { grub_free (descstrp); From 7debdce1e98907e65223a4b4c53a41345ac45e53 Mon Sep 17 00:00:00 2001 From: Jamie Date: Mon, 14 Jul 2025 10:07:47 +0100 Subject: [PATCH 165/208] commands/usbtest: Ensure string length is sufficient in usb string processing If descstrp->length is less than 2 this will result in underflow in "descstrp->length / 2 - 1" math. Let's fix the check to make sure the value is sufficient. Signed-off-by: Jamie Reviewed-by: Daniel Kiper --- grub-core/commands/usbtest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/commands/usbtest.c b/grub-core/commands/usbtest.c index 8ef187a9a..3184ac9af 100644 --- a/grub-core/commands/usbtest.c +++ b/grub-core/commands/usbtest.c @@ -90,7 +90,7 @@ grub_usb_get_string (grub_usb_device_t dev, grub_uint8_t index, int langid, 0x06, (3 << 8) | index, langid, descstr.length, (char *) descstrp); - if (descstrp->length == 0) + if (descstrp->length < 2) { grub_free (descstrp); *string = grub_strdup (""); From 56ecdfc1a524872707ada9253dda7292baa2d5e0 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 17 Nov 2025 18:01:40 -0600 Subject: [PATCH 166/208] util/grub-mkrescue: Fix spelling mistakes Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- util/grub-mkrescue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/grub-mkrescue.c b/util/grub-mkrescue.c index 951b17b8e..e7b9078ab 100644 --- a/util/grub-mkrescue.c +++ b/util/grub-mkrescue.c @@ -469,7 +469,7 @@ main (int argc, char *argv[]) argp_argv[0] = argv[0]; argp_argc = 1; - /* argp doesn't allow us to catch unknwon arguments, + /* argp doesn't allow us to catch unknown arguments, so catch them before passing to argp */ { From 7d885513ffeba1f81729ff73b53892402a0d6561 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 17 Nov 2025 18:01:41 -0600 Subject: [PATCH 167/208] docs: Fix spelling, grammatical and usage issues with new Porting section There are some other fixes outside of this section as well. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- docs/grub-dev.texi | 114 ++++++++++++++++++++++----------------------- docs/grub.texi | 2 +- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi index 62ad43c9d..9a2e9d906 100644 --- a/docs/grub-dev.texi +++ b/docs/grub-dev.texi @@ -900,26 +900,26 @@ is to be run in a GDB session running with the @file{gdb_grub} GDB script. @node Porting @chapter Porting -GRUB2 is designed to be easily portable accross platforms. But because of the +GRUB2 is designed to be easily portable across platforms. But because of the nature of bootloader every new port must be done separately. Here is how I did -MIPS (loongson and ARC) and Xen ports. Note than this is more of suggestions, -not absolute truth. +MIPS (loongson and ARC) and Xen ports. Note that this is more of a suggestion, +and not absolute truth. -First of all grab any architecture specifications you can find in public -(please avoid NDA). +First of all, grab any architecture specifications you can find in the public +domain (please avoid NDA). First stage is ``Hello world''. I've done it outside of GRUB for simplicity. Your task is to have a small program which is loadable as bootloader and -clearly shows its presence to you. If you have easily accessible console -you can just print a message. If you have a mapped framebuffer you know address +clearly shows its presence to you. If you have an easily accessible console +you can just print a message. If you have a mapped framebuffer you know the address of, you can draw a square. If you have a debug facility, just hanging without -crashing might be enough. For the first stage you can choose to load the -bootloader across the network since format for network image is often easier +crashing might be enough. For the first stage, you can choose to load the +bootloader across the network since the format for a network image is often easier than for local boot and it skips the need of small intermediary stages and -nvram handling. Additionally you can often have a good idea of the needed -format by running ``file'' on any netbootable executable for given platform. +nvram handling. Additionally, you can often have a good idea of the needed +format by running ``file'' on any netbootable executable for the given platform. -This program should probably have 2 parts: an assembler and C one. Assembler one +This program should probably have 2 parts: an assembler and C one. The assembler one handles BSS cleaning and other needed setup (on some platforms you may need to switch modes or copy the executable to its definitive position). So your code may look like (x86 assembly for illustration purposes) @@ -961,12 +961,12 @@ Sometimes you need a third file: assembly stubs for ABI-compatibility. Once this file is functional it's time to move it into GRUB2. The startup assembly file goes to grub-core/kern/$cpu/$platform/startup.S. You should also -include grub/symbol.h and replace call to entry point with call to +include grub/symbol.h and replace the call to entry point with call to EXT_C(grub_main). The C file goes to grub-core/kern/$cpu/$platform/init.c and its entry point is renamed to void grub_machine_init (void). Keep final -infinite loop for now. Stubs file if any goes to +infinite loop for now. Stub files if any goes to grub-core/kern/$cpu/$platform/callwrap.S. Sometimes either $cpu or $platform -is dropped if file is used on several cpus respectivelyplatforms. +is dropped if file is used on several cpus respectively or platforms. Check those locations if they already have what you're looking for. Then modify in configure.ac the following parts: @@ -984,10 +984,10 @@ esac @end example Sometimes CPU have additional architecture names which don't influence booting. -You might want to have some canonical name to avoid having bunch of identical +You might want to have some canonical name to avoid having a bunch of identical platforms with different names. -NOTE: it doesn't influence compile optimisations which depend solely on +NOTE: It doesn't influence compile optimisations which depend solely on chosen compiler and compile options. @example @@ -1049,7 +1049,7 @@ AM_CONDITIONAL([COND_powerpc_ieee1275], [test x$target_cpu = xpowerpc -a x$platf @end example Next stop is gentpl.py. You need to add your platform to the list of supported -ones (sorry that this list is duplicated): +ones (unfortunately, this list is duplicated): @example GRUB_PLATFORMS = [ "emu", "i386_pc", "i386_efi", "i386_qemu", "i386_coreboot", @@ -1059,21 +1059,21 @@ GRUB_PLATFORMS = [ "emu", "i386_pc", "i386_efi", "i386_qemu", "i386_coreboot", "mips_qemu_mips", "s390_mainframe" ] @end example -You may also want already to add new platform to one or several of available +Now you may also want to add a new platform to one or several of available groups. In particular we always have a group for each CPU even when only one platform for given CPU is available. Then comes grub-core/Makefile.core.def. In the block ``kernel'' you'll need to define ldflags for your platform ($cpu_$platform_ldflags). You also need to -declare startup asm file ($cpu_$platform_startup) as well as any other files -(e.g. init.c and callwrap.S) (e.g. $cpu_$platform = kern/$cpu/$platform/init.c). -At this stage you will also need to add dummy dl.c and cache.S with functions +declare a startup asm file ($cpu_$platform_startup) as well as any other files, +like init.c or callwrap.S (e.g. $cpu_$platform = kern/$cpu/$platform/init.c). +At this stage, you will also need to add a dummy dl.c and cache.S with functions grub_err_t grub_arch_dl_check_header (void *ehdr), grub_err_t grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) (dl.c) and void grub_arch_sync_caches (void *address, grub_size_t len) (cache.S). They won't be used for now. -You will need to create directory include/$cpu/$platform and a file +You will need to create a directory include/$cpu/$platform and a file include/$cpu/types.h. The latter following this template: @example @@ -1097,7 +1097,7 @@ include/$cpu/types.h. The latter following this template: You will also need to add a dummy file to datetime and setjmp modules to avoid any of it having no files. It can be just completely empty at this stage. -You'll need to make grub-mkimage.c (util/grub_mkimage.c) aware of the needed +You'll need to make grub-mkimage (in util/grub_mkimage.c) aware of the needed format. For most commonly used formats like ELF, PE, aout or raw the support is already present and you'll need to make it follow the existant code paths for your platform adding adjustments if necessary. When done compile: @@ -1108,47 +1108,47 @@ for your platform adding adjustments if necessary. When done compile: make > /dev/null @end example -And create image +And create an image: @example ./grub-mkimage -d grub-core -O $format_id -o test.img @end example -And it's time to test your test.img. +And now it's time to test your test.img. -If it works next stage is to have heap, console and timer. +If it works, the next stage is to have a heap, console and timer. To have the heap working you need to determine which regions are suitable for -heap usage, allocate them from firmware and map (if applicable). Then call -grub_mm_init_region (void *start, grub_size_t s) for every of this region. -As a shortcut for early port you can allocate right after _end or have -a big static array for heap. If you do you'll probably need to come back to +heap usage, allocate them from the firmware and map them, if applicable. +Then call grub_mm_init_region (void *start, grub_size_t s) for each region. +As a shortcut for an early port, you can allocate right after _end or have +a big static array for heap. If you do, you'll probably need to come back to this later. As for output console you should distinguish between an array of -text, terminfo or graphics-based console. Many of real-world examples don't +text, terminfo or graphics-based console. Many real-world examples don't fit perfectly into any of these categories but one of the models is easier -to be used as base. In second and third case you should add your platform to +to be used as base. In the second and third case, you should add your platform to terminfokernel respectively videoinkernel group. A good example of array of -text is i386-pc (kern/i386/pc/init.c and term/i386/pc/console.c). -Of terminfo is ieee1275 (kern/ieee1275/init.c and term/ieee1275/console.c). -Of video is loongson (kern/mips/loongson/init.c). Note that terminfo has +text is i386-pc (kern/i386/pc/init.c and term/i386/pc/console.c), +of terminfo is ieee1275 (kern/ieee1275/init.c and term/ieee1275/console.c), +and of video is loongson (kern/mips/loongson/init.c). Note that terminfo has to be inited in 2 stages: one before (to get at least rudimentary console as early as possible) and another after the heap (to get full-featured console). -For the input there are string of keys, terminfo and direct hardware. For string -of keys look at i386-pc (same files), for terminfo ieee1275 (same files) and for -hardware loongson (kern/mips/loongson/init.c and term/at_keyboard.c). +For the input, there is a string of keys (eg. i386-pc, same files), terminfo +(ieee1275, same files), and hardware (loongson, see kern/mips/loongson/init.c +and term/at_keyboard.c). -For the timer you'll need to call grub_install_get_time_ms (...) with as sole -argument a function returning a grub_uint64_t of a number of milliseconds +For the timer, you'll need to call grub_install_get_time_ms (...) with the +sole argument a function returning a grub_uint64_t number of milliseconds elapsed since arbitrary point in the past. -Once these steps accomplished you can remove the inifinite loop and you should +Once these steps are accomplished, you can remove the inifinite loop and you should be able to get to the minimal console. Next step is to have module loading -working. For this you'll need to fill kern/$cpu/dl.c and kern/$cpu/cache.S +working. For this, you'll need to fill kern/$cpu/dl.c and kern/$cpu/cache.S with real handling of relocations and respectively the real sync of I and D caches. Also you'll need to decide where in the image to store the modules. Usual way is to have it concatenated at the end. In this case you'll need to modify startup.S to copy modules out of bss to let's say ALIGN_UP (_end, 8) -before cleaning out bss. You'll probably find useful to add total_module_size +before cleaning out bss. You'll probably find it useful to add total_module_size field to startup.S. In init.c you need to set grub_modbase to the address where modules can be found. You may need grub_modules_get_end () to avoid declaring the space occupied by modules as usable for heap. You can test modules @@ -1164,9 +1164,9 @@ Once this works, you should think of implementing disk access. Look around disk/ for examples. Then, very importantly, you probably need to implement the actual loader -(examples available in loader/) +(examples available in loader/). -Last step to have minimally usable port is to add support to grub-install to +The last step to have a minimally usable port is to add support to grub-install to put GRUB in a place where firmware or platform will pick it up. Next steps are: filling datetime.c, setjmp.S, network (net/drivers), @@ -1175,15 +1175,15 @@ video (video/), halt (lib/), reboot (lib/). Please add your platform to Platform limitations and Supported kernels chapter in user documentation and mention any steps you skipped which result in reduced features or performance. Here is the quick checklist of features. Some of them -are less important than others and skipping them is completely ok, just needs +are less important than others and skipping them is completely ok, it just needs to be mentioned in user documentation. Checklist: @itemize -@item Is heap big enough? -@item Which charset is supported by console? -@item Does platform have disk driver? -@item Do you have network card support? +@item Is the heap big enough? +@item Which charset is supported by the console? +@item Does the platform have disk driver? +@item Is there network card support? @item Are you able to retrieve datetime (with date)? @item Are you able to set datetime (with date)? @item Is serial supported? @@ -1209,16 +1209,16 @@ GRUB? @node Error Handling @chapter Error Handling -Error handling in GRUB 2 is based on exception handling model. As C language +Error handling in GRUB 2 is based on exception handling model. As the C language doesn't directly support exceptions, exception handling behavior is emulated -in software. +in software. -When exception is raised, function must return to calling function. If calling +When an exception is raised, the function must return to the calling function. If the calling function does not provide handling of the exception it must return back to its -calling function and so on, until exception is handled. If exception is not -handled before prompt is displayed, error message will be shown to user. +calling function and so on, until the exception is handled. If the exception is not +handled before a prompt is displayed, error message will be shown to user. -Exception information is stored on @code{grub_errno} global variable. If +Exception information is stored in the @code{grub_errno} global variable. If @code{grub_errno} variable contains value @code{GRUB_ERR_NONE}, there is no active exception and application can continue normal processing. When @code{grub_errno} has other value, it is required that application code either handles this error or diff --git a/docs/grub.texi b/docs/grub.texi index d4287181c..7181009b6 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -2950,7 +2950,7 @@ Heavily limited platforms: Lightly limited platforms: @itemize -@item *-xen: limited only by adress space and RAM size. +@item *-xen: limited only by address space and RAM size. @item i386-qemu: kernel.img (.text + .data + .bss) is limited by 392704 bytes. (core.img would be limited by ROM size but it's unlimited on qemu @item All EFI platforms: limited by contiguous RAM size and possibly firmware bugs From db16859e8e859171763bc71bb1f1a4e6e67af169 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 17 Nov 2025 18:01:42 -0600 Subject: [PATCH 168/208] docs: Add note and explanation that the privileged user is required for properly running the tests Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- docs/grub-dev.texi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi index 9a2e9d906..9d256145e 100644 --- a/docs/grub-dev.texi +++ b/docs/grub-dev.texi @@ -495,6 +495,9 @@ These dependencies are currently documented in the @uref{https://git.savannah.gnu.org/cgit/grub.git/tree/INSTALL, INSTALL} file in the source repository. Once installed, the test suite can be started by running the @command{make check} command from the GRUB build directory. +To properly run all the tests, the test suite must be run as the privileged +user. For instance, the filesystem tests require creating and mounting the +tested filessytems. @node Updating External Code @chapter Updating external code From 591e02bc6e3bc6e3cc536794c44fa289f0aa0ebf Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 17 Nov 2025 18:01:43 -0600 Subject: [PATCH 169/208] docs: Reorganize test section and add section on writing tests Rename the main section to Tests and put the existing test section into a subsection. A new subsection called "Writing tests" is added to give a brief overview and make clear the difference in returning a SKIP code versus a HARD ERROR code. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- docs/grub-dev.texi | 55 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi index 9d256145e..51a0923ec 100644 --- a/docs/grub-dev.texi +++ b/docs/grub-dev.texi @@ -77,7 +77,7 @@ This edition documents version @value{VERSION}. * Coding style:: * Finding your way around:: * Contributing Changes:: -* Setting up and running test suite:: +* Tests:: * Updating External Code:: * Debugging:: * Porting:: @@ -485,8 +485,17 @@ If your intention is to just get started, please do not submit a inclusion request. Instead, please subscribe to the mailing list, and communicate first (e.g. sending a patch, asking a question, commenting on another message...). +@node Tests +@chapter Tests + +@menu +* Setting up and running test suite:: +* Writing new tests:: +@end menu + + @node Setting up and running test suite -@chapter Setting up and running test suite +@section Setting up and running test suite GRUB is basically a tiny operating system with read support for many file systems and which has been ported to a variety of architectures. As such, its @@ -496,8 +505,46 @@ These dependencies are currently documented in the file in the source repository. Once installed, the test suite can be started by running the @command{make check} command from the GRUB build directory. To properly run all the tests, the test suite must be run as the privileged -user. For instance, the filesystem tests require creating and mounting the -tested filessytems. +user, for instance to run the filesystem tests, which require mounting +filesystem images. Of course, virtualization or containers may be used, but +this requires extra configuration outside the scope of this document. + +@node Writing new tests +@section Writing new tests + +There are two kinds of tests in the GRUB test suite: native and non-native. +Native tests are those which run on the build architecture and non-native +run on the target architecture. The non-native tests are run in a QEMU +virtual machine using the grub-shell script in tests/util. When writing a +new test, first determine if it should run on the host or the target and +then look at the existing tests as examples of how to they should be written. + +The GRUB test suite uses automake (@uref{https://www.gnu.org/software/automake/manual/automake.html#Tests, see documention here}). One thing of importance +to note is that tests have 4 classes of return codes: SKIP (77), HARD ERROR +(99), PASS (0), and FAIL (all other codes). A +@uref{https://www.gnu.org/software/automake/manual/automake.html#index-test-skip, SKIP return code} +should be returned when this test cannot be performed and thus should be +skipped. Typically this is because the target does not support the test, +such as the ohci USB test for the powerpc-ieee1275 target because there +are no native drivers for that target. A +@uref{https://www.gnu.org/software/automake/manual/automake.html#index-Distinction-between-errors-and-failures-in-testsuites, HARD ERROR return code} +should be returned when a failure in something other than what is intended +to be tested happens. This is commonly returned when the test cannot be +properly run because of deficiencies in the test environment, eg. when +testing the xfs filesystem, but the kernel has no support for mounting xfs +volumes. A SKIP should never be returned for a HARD ERROR condition +because at best the person running the test does not know if the test was +skipped because it doesn't apply to the target or because the tester failed +to setup the environment properly. At worst, the tester will believe that +everything is okay without realizing that the tests are not covering all +the code that it should. + +Keep portability in mind while creating a new tests so that the tests can be +run on a variety of systems and shells. Do not use bashisms. Also try to avoid +using utilities that would unecessarily add software dependencies. Sometimes +this is unavoidable. Copy or adapt existing test implementations where feasible. +If the test is native and requires root, make sure to check that the test is +run with the root user and return HARD ERROR if it is not. @node Updating External Code @chapter Updating external code From f41c896d2345bee41e4db9208e1dbb7f8d621c02 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 17 Nov 2025 18:01:44 -0600 Subject: [PATCH 170/208] INSTALL: Make note that Linux kernel 6.12.x or earlier is needed for reiserfs testing Also, remove wording suggesting that tests may be skipped if prerequisites are not installed. Tests should never be skipped because of an environment misconfiguration, instead they should return a hard error (code 99). Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- INSTALL | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/INSTALL b/INSTALL index 22b68cb6c..4262bf3cd 100644 --- a/INSTALL +++ b/INSTALL @@ -66,7 +66,7 @@ More cross compiling toolchains can be found at the following trusted sites: * https://mirrors.kernel.org/pub/tools/crosstool/ * https://toolchains.bootlin.com/ -Prerequisites for make-check: +Prerequisites for running make-check successfully: * qemu, specifically the binary "qemu-system-ARCH" where ARCH is the architecture GRUB has been built for; the "qemu-system" package on Debian @@ -86,6 +86,8 @@ Prerequisites for make-check: reiserfs, udf, xfs - On newer kernels, the exfat kernel modules may be used instead of the exfat FUSE filesystem + - Kernel version 6.12.x was the last series to include reiserfs support, + so later kernels will fail the reiser filesystem test. * The following are Debian named packages required mostly for the full suite of filesystem testing (but some are needed by other tests as well): - btrfs-progs, dosfstools, e2fsprogs, erofs-utils, exfatprogs, exfat-fuse, @@ -96,8 +98,8 @@ Prerequisites for make-check: - attr, cpio, g++, gawk, parted, recode, tar, util-linux Note that `make check' will run and many tests may complete successfully -with only a subset of these prerequisites. However, some tests may be -skipped or fail due to missing prerequisites. +with only a subset of these prerequisites. However, some tests may fail +due to missing prerequisites. To build the documentation you'll need: * texinfo, for the info and html documentation From 1a5417f39a0ccefcdd5440f2a67f84d2d2e26960 Mon Sep 17 00:00:00 2001 From: Nicholas Vinson Date: Tue, 18 Nov 2025 19:38:07 -0500 Subject: [PATCH 171/208] configure: Check linker for --image-base support In several scenarios, configure tests assume it's safe to use "-Wl,-Ttext,
", but starting with ld.lld-21, blindly using that flag may result in configure-test failures due to ld.lld failing to link. The failure is because ld.lld-21 no longer allows the specified address is less than the base address. However, ld.lld-21+ and ld.bfd-2.44+ both provide support for the --image-base flag making it preferable over the older -Ttext flag. Fixes: https://savannah.gnu.org/bugs/?67662 Signed-off-by: Nicholas Vinson Reviewed-by: Daniel Kiper --- acinclude.m4 | 5 +++++ configure.ac | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index fa7840f09..70c1912f8 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -79,6 +79,11 @@ AC_DEFUN([grub_PROG_OBJCOPY_ABSOLUTE], [AC_MSG_CHECKING([whether ${TARGET_OBJCOPY} works for absolute addresses]) AC_CACHE_VAL(grub_cv_prog_objcopy_absolute, [cat > conftest.c <<\EOF +asm ( + ".globl start, _start, __start\n" + ".ifdef cmain; .set start = _start = __start = cmain\n.endif\n" + ".ifdef _cmain; .set start = _start = __start = _cmain\n.endif\n" +); void cmain (void); void cmain (void) diff --git a/configure.ac b/configure.ac index 17937baf4..a282bf7bf 100644 --- a/configure.ac +++ b/configure.ac @@ -1461,7 +1461,6 @@ elif test x$grub_cv_target_cc_link_format = x-mi386pe || test x$grub_cv_target_c TARGET_IMG_LDSCRIPT='$(top_srcdir)'"/conf/i386-cygwin-img-ld.sc" TARGET_IMG_LDFLAGS="-Wl,-T${TARGET_IMG_LDSCRIPT}" TARGET_IMG_LDFLAGS_AC="-Wl,-T${srcdir}/conf/i386-cygwin-img-ld.sc" - TARGET_IMG_BASE_LDOPT="-Wl,-Ttext" TARGET_IMG_CFLAGS= else TARGET_APPLE_LINKER=0 @@ -1469,7 +1468,6 @@ else TARGET_IMG_LDSCRIPT= TARGET_IMG_LDFLAGS='-Wl,-N' TARGET_IMG_LDFLAGS_AC='-Wl,-N' - TARGET_IMG_BASE_LDOPT="-Wl,-Ttext" TARGET_IMG_CFLAGS= fi @@ -1795,6 +1793,18 @@ LIBS="" grub_ASM_USCORE grub_PROG_TARGET_CC if test "x$TARGET_APPLE_LINKER" != x1 ; then +AX_CHECK_LINK_FLAG([-Wl,--image-base,0x400000], + [TARGET_IMG_BASE_LDOPT="-Wl,--image-base"], + [TARGET_IMG_BASE_LDOPT="-Wl,-Ttext"], + [], + [AC_LANG_SOURCE([ +asm (".globl start; start:"); +asm (".globl _start; _start:"); +asm (".globl __start; __start:"); +void __main (void); +void __main (void) {} +int main (void); + ])]) grub_PROG_OBJCOPY_ABSOLUTE fi grub_PROG_LD_BUILD_ID_NONE From ae69b464bedfdf4da9147124dce28cbebf3bb3d9 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Wed, 19 Nov 2025 06:37:32 +0000 Subject: [PATCH 172/208] lib/relocator: Fix dereference after NULL check In the function free_subchunk(), after checking that subchu->post isn't NULL, grub_memset() is called on subchu->pre->freebytes but it should be called on subchu->post->freebytes. If subchu->pre is NULL but subchu->post isn't NULL, then this could lead to a NULL pointer dereference. Fixes: CID 473882 Signed-off-by: Vladimir Serbinenko Signed-off-by: Alec Brown Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/lib/relocator.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grub-core/lib/relocator.c b/grub-core/lib/relocator.c index 1e1e09704..37da0c6db 100644 --- a/grub-core/lib/relocator.c +++ b/grub-core/lib/relocator.c @@ -398,9 +398,9 @@ free_subchunk (const struct grub_relocator_subchunk *subchu) if (subchu->post) { int off = subchu->start + subchu->size - fend; - grub_memset (subchu->pre->freebytes, - 0xff, sizeof (subchu->pre->freebytes) - off / 8); - subchu->pre->freebytes[off / 8] |= ((1 << (8 - (off % 8))) - 1); + grub_memset (subchu->post->freebytes, + 0xff, sizeof (subchu->post->freebytes) - off / 8 - 1); + subchu->post->freebytes[sizeof (subchu->post->freebytes) - off / 8 - 1] |= ((1 << (8 - (off % 8))) - 1); check_leftover (subchu->post); } #endif From 07c250487fea48fb934efab2bdfe32e9339cfabf Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Wed, 19 Nov 2025 15:30:47 +0530 Subject: [PATCH 173/208] osdep/linux/ofpath: Add missing strdup() failure checks Segmentation faults or undefined behaviour may result from a NULL pointer dereference in strip_trailing_digits() and grub_util_devname_to_ofpath() if strdup() fails. Therefore, I added a NULL check to fix this. Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Srish Srinivasan Reviewed-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/osdep/linux/ofpath.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/grub-core/osdep/linux/ofpath.c b/grub-core/osdep/linux/ofpath.c index a6153d359..24a4d5c8d 100644 --- a/grub-core/osdep/linux/ofpath.c +++ b/grub-core/osdep/linux/ofpath.c @@ -695,6 +695,9 @@ strip_trailing_digits (const char *p) char *new, *end; new = strdup (p); + if (new == NULL) + return NULL; + end = new + strlen(new) - 1; while (end >= new) { @@ -709,13 +712,18 @@ strip_trailing_digits (const char *p) char * grub_util_devname_to_ofpath (const char *sys_devname) { - char *name_buf, *device, *devnode, *devicenode, *ofpath; + char *name_buf, *device, *devnode, *devicenode, *ofpath = NULL; name_buf = xrealpath (sys_devname); device = get_basename (name_buf); devnode = strip_trailing_digits (name_buf); + if (devnode == NULL) + goto devnode_fail; + devicenode = strip_trailing_digits (device); + if (devicenode == NULL) + goto devicenode_fail; if (device[0] == 'h' && device[1] == 'd') ofpath = of_path_of_ide(name_buf, device, devnode, devicenode); @@ -741,8 +749,12 @@ grub_util_devname_to_ofpath (const char *sys_devname) ofpath = NULL; } - free (devnode); free (devicenode); + + devicenode_fail: + free (devnode); + + devnode_fail: free (name_buf); return ofpath; From 14376470520d3106e32f5c0db4d15a86e7eaf33a Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Wed, 19 Nov 2025 13:11:34 -0600 Subject: [PATCH 174/208] Revert "tests: Skip tests if required tools are not available" As explained in commit a21618c8a (tests: Test aborts due to missing requirements should be marked as error instead of skipped) and in the Automake manual[1], skipped tests are tests that should not be run, e.g. running the ohci test on the powerpc-ieee1275 as there are no native ohci drivers for that platform. Test that fail for reasons other than there is a bug in GRUB code that is causing the test to fail are hard errors. Commonly this is because the test is run in an improperly configured environment, like required programs are missing. If a hard error condition is identified with a SKIP return code, the person running the tests can not know without investigating every skip if a SKIP in the tests was because the test does not apply to the target being tested or because the user had a misconfigured environment that was causing the test not to run. By ensuring that a test is skipped only when it should not run, the person running the test can be sure that there is no need to investigate why the test was skipped. This reverts commit bf13fed5f (tests: Skip tests if required tools are not available). [1] https://www.gnu.org/software/automake/manual/automake.html#Generalities-about-Testing Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/btrfs_test.in | 2 +- tests/cpio_test.in | 2 +- tests/erofs_test.in | 2 +- tests/exfat_test.in | 2 +- tests/ext234_test.in | 6 +++--- tests/f2fs_test.in | 2 +- tests/fat_test.in | 2 +- tests/grub_cmd_cryptomount.in | 4 ++-- tests/gzcompress_test.in | 2 +- tests/hfs_test.in | 2 +- tests/hfsplus_test.in | 2 +- tests/iso9660_test.in | 2 +- tests/jfs_test.in | 2 +- tests/luks1_test.in | 4 ++-- tests/luks2_test.in | 4 ++-- tests/lzocompress_test.in | 2 +- tests/minixfs_test.in | 2 +- tests/nilfs2_test.in | 2 +- tests/ntfs_test.in | 4 ++-- tests/partmap_test.in | 2 +- tests/reiserfs_test.in | 2 +- tests/romfs_test.in | 2 +- tests/squashfs_test.in | 2 +- tests/tar_test.in | 2 +- tests/tpm2_key_protector_test.in | 4 ++-- tests/udf_test.in | 2 +- tests/xfs_test.in | 2 +- tests/xzcompress_test.in | 2 +- tests/zfs_test.in | 2 +- 29 files changed, 36 insertions(+), 36 deletions(-) diff --git a/tests/btrfs_test.in b/tests/btrfs_test.in index 84cfd95d8..900f56379 100644 --- a/tests/btrfs_test.in +++ b/tests/btrfs_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.btrfs >/dev/null 2>&1; then echo "mkfs.btrfs not installed; cannot test btrfs." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" btrfs diff --git a/tests/cpio_test.in b/tests/cpio_test.in index fb468564a..74759419e 100644 --- a/tests/cpio_test.in +++ b/tests/cpio_test.in @@ -4,7 +4,7 @@ set -ex if ! which cpio >/dev/null 2>&1; then echo "cpio not installed; cannot test cpio." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" cpio_bin diff --git a/tests/erofs_test.in b/tests/erofs_test.in index f8cf3bb8b..e0dec8d3a 100644 --- a/tests/erofs_test.in +++ b/tests/erofs_test.in @@ -4,7 +4,7 @@ set -ex if ! which mkfs.erofs >/dev/null 2>&1; then echo "mkfs.erofs not installed; cannot test erofs." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" erofs_compact diff --git a/tests/exfat_test.in b/tests/exfat_test.in index ae2b209a1..63300b8a5 100644 --- a/tests/exfat_test.in +++ b/tests/exfat_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.exfat >/dev/null 2>&1; then echo "mkfs.exfat not installed; cannot test exFAT." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" exfat diff --git a/tests/ext234_test.in b/tests/ext234_test.in index 2f8aafc9c..3761238db 100644 --- a/tests/ext234_test.in +++ b/tests/ext234_test.in @@ -12,17 +12,17 @@ fi if ! which mkfs.ext2 >/dev/null 2>&1; then echo "mkfs.ext2 not installed; cannot test ext2." - exit 77 + exit 99 fi if ! which mkfs.ext3 >/dev/null 2>&1; then echo "mkfs.ext3 not installed; cannot test ext3." - exit 77 + exit 99 fi if ! which mkfs.ext4 >/dev/null 2>&1; then echo "mkfs.ext4 not installed; cannot test ext4." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" ext2_old diff --git a/tests/f2fs_test.in b/tests/f2fs_test.in index 4d16c2fab..0896810ea 100644 --- a/tests/f2fs_test.in +++ b/tests/f2fs_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.f2fs >/dev/null 2>&1; then echo "mkfs.f2fs not installed; cannot test f2fs." - exit 77 + exit 99 fi diff --git a/tests/fat_test.in b/tests/fat_test.in index 0e4b1ebae..966b2c9db 100644 --- a/tests/fat_test.in +++ b/tests/fat_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.vfat >/dev/null 2>&1; then echo "mkfs.vfat not installed; cannot test FAT." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" vfat16a diff --git a/tests/grub_cmd_cryptomount.in b/tests/grub_cmd_cryptomount.in index 97863c3e8..bcba7c74c 100644 --- a/tests/grub_cmd_cryptomount.in +++ b/tests/grub_cmd_cryptomount.in @@ -27,12 +27,12 @@ fi if ! which cryptsetup >/dev/null 2>&1; then echo "cryptsetup not installed; cannot test cryptomount." - exit 77 + exit 99 fi if ! which mkfs.vfat >/dev/null 2>&1; then echo "mkfs.vfat not installed; cannot test cryptomount." - exit 77 + exit 99 fi COMMON_OPTS='${V:+--debug=$V} --cs-opts="--pbkdf-force-iterations 1000"' diff --git a/tests/gzcompress_test.in b/tests/gzcompress_test.in index 471b6537e..0f80037fb 100644 --- a/tests/gzcompress_test.in +++ b/tests/gzcompress_test.in @@ -21,7 +21,7 @@ grubshell=@builddir@/grub-shell if ! which gzip >/dev/null 2>&1; then echo "gzip not installed; cannot test gzip compression." - exit 77 + exit 99 fi v=$(echo hello | "${grubshell}" --mkrescue-arg=--compress=gz) diff --git a/tests/hfs_test.in b/tests/hfs_test.in index fc0e0e75c..99432114f 100644 --- a/tests/hfs_test.in +++ b/tests/hfs_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.hfs >/dev/null 2>&1; then echo "mkfs.hfs not installed; cannot test HFS." - exit 77 + exit 99 fi if ! grep -q mac_roman /proc/modules && ! modprobe mac_roman; then diff --git a/tests/hfsplus_test.in b/tests/hfsplus_test.in index 7918a68bb..402d1cd66 100644 --- a/tests/hfsplus_test.in +++ b/tests/hfsplus_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.hfsplus >/dev/null 2>&1; then echo "mkfs.hfsplus not installed; cannot test hfsplus." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" hfsplus diff --git a/tests/iso9660_test.in b/tests/iso9660_test.in index 7a6816f94..a36f3022a 100644 --- a/tests/iso9660_test.in +++ b/tests/iso9660_test.in @@ -4,7 +4,7 @@ set -ex if ! which xorriso >/dev/null 2>&1; then echo "xorriso not installed; cannot test iso9660." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" joliet diff --git a/tests/jfs_test.in b/tests/jfs_test.in index b1c18e9b1..07f1ca193 100644 --- a/tests/jfs_test.in +++ b/tests/jfs_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.jfs >/dev/null 2>&1; then echo "mkfs.jfs not installed; cannot test JFS." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" jfs diff --git a/tests/luks1_test.in b/tests/luks1_test.in index 3344746c1..1df4a2f5f 100644 --- a/tests/luks1_test.in +++ b/tests/luks1_test.in @@ -12,12 +12,12 @@ fi if ! which mkfs.ext2 >/dev/null 2>&1; then echo "mkfs.ext2 not installed; cannot test luks." - exit 77 + exit 99 fi if ! which cryptsetup >/dev/null 2>&1; then echo "cryptsetup not installed; cannot test luks." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" luks1 diff --git a/tests/luks2_test.in b/tests/luks2_test.in index 5368698f8..75ac2c191 100644 --- a/tests/luks2_test.in +++ b/tests/luks2_test.in @@ -12,12 +12,12 @@ fi if ! which mkfs.ext2 >/dev/null 2>&1; then echo "mkfs.ext2 not installed; cannot test luks2." - exit 77 + exit 99 fi if ! which cryptsetup >/dev/null 2>&1; then echo "cryptsetup not installed; cannot test luks2." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" luks2 diff --git a/tests/lzocompress_test.in b/tests/lzocompress_test.in index 50145dd0c..a89d014e9 100644 --- a/tests/lzocompress_test.in +++ b/tests/lzocompress_test.in @@ -21,7 +21,7 @@ grubshell=@builddir@/grub-shell if ! which lzop >/dev/null 2>&1; then echo "lzop not installed; cannot test lzo compression." - exit 77 + exit 99 fi v=$(echo hello | "${grubshell}" --mkrescue-arg=--compress=lzo) diff --git a/tests/minixfs_test.in b/tests/minixfs_test.in index 0d28a7226..780f699fa 100644 --- a/tests/minixfs_test.in +++ b/tests/minixfs_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.minix >/dev/null 2>&1; then echo "mkfs.minix not installed; cannot test minixfs." - exit 77 + exit 99 fi if ! mkfs.minix -h | grep -- -v > /dev/null; then diff --git a/tests/nilfs2_test.in b/tests/nilfs2_test.in index 84b8b1a0b..95ec7c3b4 100644 --- a/tests/nilfs2_test.in +++ b/tests/nilfs2_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.nilfs2 >/dev/null 2>&1; then echo "mkfs.nilfs2 not installed; cannot test nilfs2." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" nilfs2 diff --git a/tests/ntfs_test.in b/tests/ntfs_test.in index 5b1bebe16..b6432d2a1 100644 --- a/tests/ntfs_test.in +++ b/tests/ntfs_test.in @@ -12,12 +12,12 @@ fi if ! which mkfs.ntfs >/dev/null 2>&1; then echo "mkfs.ntfs not installed; cannot test ntfs." - exit 77 + exit 99 fi if ! which setfattr >/dev/null 2>&1; then echo "setfattr not installed; cannot test ntfs." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" ntfs diff --git a/tests/partmap_test.in b/tests/partmap_test.in index 8e12cf7ec..6157a8537 100644 --- a/tests/partmap_test.in +++ b/tests/partmap_test.in @@ -100,7 +100,7 @@ esac if ! which ${parted} >/dev/null 2>&1; then echo "${parted} not installed; cannot test partmap" - exit 77 + exit 99 fi imgfile="`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 99 diff --git a/tests/reiserfs_test.in b/tests/reiserfs_test.in index 48d3ab4cc..6f8ebcf29 100644 --- a/tests/reiserfs_test.in +++ b/tests/reiserfs_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.reiserfs >/dev/null 2>&1; then echo "mkfs.reiserfs not installed; cannot test reiserfs." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" reiserfs diff --git a/tests/romfs_test.in b/tests/romfs_test.in index 2c66cde34..e3b4a0210 100644 --- a/tests/romfs_test.in +++ b/tests/romfs_test.in @@ -4,7 +4,7 @@ set -ex if ! which genromfs >/dev/null 2>&1; then echo "genromfs not installed; cannot test romfs." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" romfs diff --git a/tests/squashfs_test.in b/tests/squashfs_test.in index b973f7bac..36f8e9735 100644 --- a/tests/squashfs_test.in +++ b/tests/squashfs_test.in @@ -4,7 +4,7 @@ set -ex if ! which mksquashfs >/dev/null 2>&1; then echo "mksquashfs not installed; cannot test squashfs." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" squash4_gzip diff --git a/tests/tar_test.in b/tests/tar_test.in index eafb6649d..4f0c697ac 100644 --- a/tests/tar_test.in +++ b/tests/tar_test.in @@ -4,7 +4,7 @@ set -ex if ! which tar >/dev/null 2>&1; then echo "tar not installed; cannot test tar." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" tarfs diff --git a/tests/tpm2_key_protector_test.in b/tests/tpm2_key_protector_test.in index 6dbd69787..5dd86d6ee 100644 --- a/tests/tpm2_key_protector_test.in +++ b/tests/tpm2_key_protector_test.in @@ -41,7 +41,7 @@ fi if ! command -v cryptsetup >/dev/null 2>&1; then echo "cryptsetup not installed; cannot test tpm2." - exit 77 + exit 99 fi if ! grep -q tpm_vtpm_proxy /proc/modules && ! modprobe tpm_vtpm_proxy; then @@ -51,7 +51,7 @@ fi if ! command -v swtpm >/dev/null 2>&1; then echo "swtpm not installed; cannot test tpm2." - exit 77 + exit 99 fi if ! command -v tpm2_startup >/dev/null 2>&1; then diff --git a/tests/udf_test.in b/tests/udf_test.in index b84d0b674..0723b3be0 100644 --- a/tests/udf_test.in +++ b/tests/udf_test.in @@ -12,7 +12,7 @@ fi if ! which mkudffs >/dev/null 2>&1; then echo "mkudffs not installed; cannot test UDF." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" udf diff --git a/tests/xfs_test.in b/tests/xfs_test.in index d6b3ab908..2c9e6e348 100644 --- a/tests/xfs_test.in +++ b/tests/xfs_test.in @@ -12,7 +12,7 @@ fi if ! which mkfs.xfs >/dev/null 2>&1; then echo "mkfs.xfs not installed; cannot test xfs." - exit 77 + exit 99 fi diff --git a/tests/xzcompress_test.in b/tests/xzcompress_test.in index f0ac52e2d..62f0564db 100644 --- a/tests/xzcompress_test.in +++ b/tests/xzcompress_test.in @@ -21,7 +21,7 @@ grubshell=@builddir@/grub-shell if ! which xz >/dev/null 2>&1; then echo "xz not installed; cannot test xz compression." - exit 77 + exit 99 fi v=$(echo hello | "${grubshell}" --mkrescue-arg=--compress=xz) diff --git a/tests/zfs_test.in b/tests/zfs_test.in index cd547b4d2..462a9b95f 100644 --- a/tests/zfs_test.in +++ b/tests/zfs_test.in @@ -12,7 +12,7 @@ fi if ! which zpool >/dev/null 2>&1; then echo "zpool not installed; cannot test zfs." - exit 77 + exit 99 fi "@builddir@/grub-fs-tester" zfs From a8b2beedf684f0200e252efe9ba5fad2665b2a36 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Wed, 19 Nov 2025 13:11:35 -0600 Subject: [PATCH 175/208] Revert "tests: Remove -w param from mkfs.hfsplus command" The original commit removes testing of GRUB's support for HFS+ wrapping and replaces it with testing that is an exact duplicate of another test, namely HFS+ without wrapping. To start, the change is misleading in that it suggests that the testing of HFS+ wrapping is still taking place, when it is not. If it was desired to remove support for testing the HFS+ wrapping, then the test should have been removed entirely. Second, having a series of tests that are exactly the same is just a waste of testing resources. And third, the justification for the change is nonsensical. Just because a required program may not have a required feature on a particular distro is not a reason that a test should be removed. Reducing test coverage because some distros do not have the tools GRUB needs to run certain tests goes against the testing priority to have test coverage be as broad as possible. The fact is that Debian, the officially supported distro for running the tests, does have a mkfs.hfsplus that supports the -w parameter. This reverts commit 2bc0929a2 (tests: Remove -w param from mkfs.hfsplus command). Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/util/grub-fs-tester.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in index bfb7b0553..7199322c6 100644 --- a/tests/util/grub-fs-tester.in +++ b/tests/util/grub-fs-tester.in @@ -735,7 +735,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do "mkfs.hfsplus" -b $BLKSIZE -v "$FSLABEL" "${MOUNTDEVICE}" dd if=/dev/urandom of="${MOUNTDEVICE}" bs=1 seek=$((0x468)) conv=notrunc count=8 ;; x"hfsplus_wrap") - "mkfs.hfsplus" -b $BLKSIZE -v "$FSLABEL" "${MOUNTDEVICE}" + "mkfs.hfsplus" -w -b $BLKSIZE -v "$FSLABEL" "${MOUNTDEVICE}" dd if=/dev/urandom of="${MOUNTDEVICE}" bs=1 seek=$((0x468)) conv=notrunc count=8 MOUNTFS="hfsplus";; x"hfsplus_casesens") From 8a850f47d070a19bac355807f16461bf52807510 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Thu, 20 Nov 2025 14:25:50 +0800 Subject: [PATCH 176/208] blsuki: Fix grub_errno leakage in blsuki_is_default_entry() The grub_strtol() call in blsuki_is_default_entry() can set grub_errno to either GRUB_ERR_BAD_NUMBER or GRUB_ERR_OUT_OF_RANGE if the input string is invalid or out of range. This grub_errno value is currently left uncleared, which can lead to unexpected behavior in subsequent functions that rely on checking current state of grub_errno. Clear grub_errno unconditionally when grub_strtol() reports error so that we can plug the leak. Signed-off-by: Michael Chang Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Avnish Chouhan Reviewed-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/commands/blsuki.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c index d089dec62..a169e3f59 100644 --- a/grub-core/commands/blsuki.c +++ b/grub-core/commands/blsuki.c @@ -1360,6 +1360,10 @@ blsuki_is_default_entry (const char *def_entry, grub_blsuki_entry_t *entry, int return true; def_idx = grub_strtol (def_entry, &def_entry_end, 0); + + /* Clear grub_errno so we can plug the leak. */ + grub_errno = GRUB_ERR_NONE; + if (*def_entry_end != '\0' || def_idx < 0 || def_idx > GRUB_INT_MAX) return false; From cb811bdf053d5e976e50673bd4685a64f63f844c Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Thu, 20 Nov 2025 13:41:41 +0530 Subject: [PATCH 177/208] normal/cmdline: Add grub_calloc() failure check and fix hist_lines state loss If grub_calloc() fails hist_lines becomes NULL. It means we loose the reference to the previously allocated hist_lines and leak memory. With this change on failure hist_lines still points to the old memory. So, no leak, no state corruption. Signed-off-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/normal/cmdline.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/grub-core/normal/cmdline.c b/grub-core/normal/cmdline.c index 9c6d9ade9..0875250be 100644 --- a/grub-core/normal/cmdline.c +++ b/grub-core/normal/cmdline.c @@ -42,7 +42,14 @@ grub_err_t grub_set_history (int newsize) { grub_uint32_t **old_hist_lines = hist_lines; + hist_lines = grub_calloc (newsize, sizeof (grub_uint32_t *)); + if (hist_lines == NULL) + { + /* We need to restore hist_lines to avoid memory leak and state loss. */ + hist_lines = old_hist_lines; + return grub_errno; + } /* Copy the old lines into the new buffer. */ if (old_hist_lines) From 8a6ea7ab7bed6ed39316897933b8bcf1936ab206 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Thu, 20 Nov 2025 12:56:45 -0600 Subject: [PATCH 178/208] bootstrap: Run linguas.sh in bootstrap epilogue Heretofore, linguas.sh had to be run by the user and a common mistake made when building GRUB was to not run the command. By adding it to the bootstrap epilogue it will by default get run at the end of the bootstrap script. The user no longer needs to remember to run it. If the --skip-po option is passed to bootstrap, do not run linguas.sh. This allows for bootstrap to be run without updating the translations, which might be desired in the future if we track po files so that translations can be used as they were at time of release. Update INSTALL file to reflect that it is no longer necessary to run linguas.sh. Also, fix a list numbering error. Fixes: 9f73ebd49be (* INSTALL: Document linguas.sh.) Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- INSTALL | 17 +++++++---------- bootstrap.conf | 8 +++++++- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/INSTALL b/INSTALL index 4262bf3cd..c9bb0b888 100644 --- a/INSTALL +++ b/INSTALL @@ -142,15 +142,12 @@ The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code. - 2. Skip this and following step if you use release tarball and proceed to - step 4. If you want translations type `./linguas.sh'. - - 3. Type `./bootstrap'. + 2. Type `./bootstrap'. The autogen.sh (called by bootstrap) uses python. By default autodetect it, but it can be overridden by setting the PYTHON variable. - 4. Type `./configure' to configure the package for your system. + 3. Type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. @@ -158,19 +155,19 @@ The simplest way to compile this package is: Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. - 6. Type `make' to compile the package. + 4. Type `make' to compile the package. - 7. Optionally, type `make check' to run any self-tests that come with + 5. Optionally, type `make check' to run any self-tests that come with the package. Note that many of the tests require root privileges in order to run. - 8. Type `make install' to install the programs and any data files and + 6. Type `make install' to install the programs and any data files and documentation. - 9. Type `make html' or `make pdf' to generate the html or pdf + 7. Type `make html' or `make pdf' to generate the html or pdf documentation. Note, these are not built by default. - 10. You can remove the program binaries and object files from the + 8. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is diff --git a/bootstrap.conf b/bootstrap.conf index 8eff35b45..e894666fd 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -63,7 +63,7 @@ checkout_only_file= copy=true vc_ignore= -SKIP_PO=t +SKIP_PO=n # Build prerequisites buildreq="\ @@ -108,4 +108,10 @@ bootstrap_post_import_hook () { bootstrap_epilogue () { mv INSTALL.grub INSTALL + + # Update translation files and create LINGUAS file used to determine + # the set of languages used to translate. + if [ "x$SKIP_PO" = "xn" ]; then + ./linguas.sh + fi } From 0dfec2945ae4847de4e7e0d607cde6c144ac9ee1 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Thu, 20 Nov 2025 12:56:46 -0600 Subject: [PATCH 179/208] bootstrap: Migrate linguas.sh into bootstrap.conf Bootstrap has infrastructure for downloading/updating project po files and generating the LINGUAS file. It uses wget instead of rsync, but provides the same functionality, namely that only po files that have a modification date before the corresponding one on the server will get redownloaded. Bootstrap creates a pristine copy of the po files in po/.reference, so update .gitignore to ignore that directory. Bootstrap also creates the po/LINGUAS file, but it does not know to add in GRUB's autogenerated po files. So move that code from linguas.sh into the bootstrap epilogue. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- bootstrap.conf | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/bootstrap.conf b/bootstrap.conf index e894666fd..79d4248c8 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -63,8 +63,6 @@ checkout_only_file= copy=true vc_ignore= -SKIP_PO=n - # Build prerequisites buildreq="\ autoconf 2.64 @@ -109,9 +107,23 @@ bootstrap_post_import_hook () { bootstrap_epilogue () { mv INSTALL.grub INSTALL - # Update translation files and create LINGUAS file used to determine - # the set of languages used to translate. - if [ "x$SKIP_PO" = "xn" ]; then - ./linguas.sh + if [ "x$SKIP_PO" = "x" ]; then + # Generate LINGUAS with all supported languages. Bootstrap will + # generate a LINGUAS, but it will not contain the autogenerated + # languages. + autogenerated="en@quot en@hebrew de@hebrew en@cyrillic en@greek en@arabic en@piglatin de_CH" + + for x in $autogenerated; do + rm -f "po/$x.po"; + done + + ( + ( + cd po && ls *.po| cut -d. -f1 + for x in $autogenerated; do + echo "$x"; + done + ) | sort | uniq | xargs + ) >po/LINGUAS fi } From 0a31df119d9f75fa357960a0f247b2795ba71656 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Thu, 20 Nov 2025 12:56:47 -0600 Subject: [PATCH 180/208] bootstrap: Condense and simplify LINGUAS generation Remove unnecessary subshells. Loop over autogenerated po files only once. Use existing LINGUAS created by bootstrap instead of finding po files again. Add wget as a soft requirement now that we are using bootstrap's code for updating translation files. This should only be needed if updated translations are desired, which is the default. There should be older translation files already, and wget is not necessary if those will suffice. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- INSTALL | 1 + bootstrap.conf | 23 +++++++++++------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/INSTALL b/INSTALL index c9bb0b888..2767fd450 100644 --- a/INSTALL +++ b/INSTALL @@ -25,6 +25,7 @@ configuring the GRUB. * Flex 2.5.35 or later * pkg-config * GNU patch +* wget (for downloading updated translations) * Other standard GNU/Unix tools * a libc with large file support (e.g. glibc 2.1 or later) diff --git a/bootstrap.conf b/bootstrap.conf index 79d4248c8..40e0b0cf4 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -113,17 +113,16 @@ bootstrap_epilogue () { # languages. autogenerated="en@quot en@hebrew de@hebrew en@cyrillic en@greek en@arabic en@piglatin de_CH" - for x in $autogenerated; do - rm -f "po/$x.po"; - done - - ( - ( - cd po && ls *.po| cut -d. -f1 - for x in $autogenerated; do - echo "$x"; - done - ) | sort | uniq | xargs - ) >po/LINGUAS + { + # NOTE: xargs has no POSIX compliant way to avoid running the program + # given as an argument when there are no input lines. So ensure that + # basename is always run with at least one argument, the empty string, + # and ignore the first line of output. + ls po/*.po | xargs -L 100 basename -s .po -a "" | tail -n +2 + for x in $autogenerated; do + rm -f "po/$x.po" + echo "$x" + done + } | sort | uniq | xargs >po/LINGUAS fi } From 29f3131a3632c70129a29d924fcb8ac98f08ee2b Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Thu, 20 Nov 2025 12:56:48 -0600 Subject: [PATCH 181/208] INSTALL: Fix a grammatical error Also, add more documentation mentioning that the tests require a "specially crafted environment" to run. Just running as root is not enough. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- INSTALL | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/INSTALL b/INSTALL index 2767fd450..7e5397f58 100644 --- a/INSTALL +++ b/INSTALL @@ -145,8 +145,8 @@ The simplest way to compile this package is: 2. Type `./bootstrap'. - The autogen.sh (called by bootstrap) uses python. By default autodetect - it, but it can be overridden by setting the PYTHON variable. + The autogen.sh (called by bootstrap) uses python. By default it is + autodetected, but it can be overridden by setting the PYTHON variable. 3. Type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might @@ -159,8 +159,8 @@ The simplest way to compile this package is: 4. Type `make' to compile the package. 5. Optionally, type `make check' to run any self-tests that come with - the package. Note that many of the tests require root privileges in - order to run. + the package. Note that many of the tests require root privileges and + a specially crafted environment in order to run. 6. Type `make install' to install the programs and any data files and documentation. From a90ccbac677db62fc0c1940cda4388370ac8a04b Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Fri, 12 Dec 2025 00:21:59 -0600 Subject: [PATCH 182/208] INSTALL: Add note that the GNU Autoconf Archive may be needed As of 1a5417f39a0c (configure: Check linker for --image-base support), the GNU Autoconf Archive is now required to bootstrap GRUB. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- INSTALL | 1 + 1 file changed, 1 insertion(+) diff --git a/INSTALL b/INSTALL index 7e5397f58..7ac1c7cc8 100644 --- a/INSTALL +++ b/INSTALL @@ -56,6 +56,7 @@ need the following. * Python 3 (NOTE: python 2.6 should still work, but it's not tested) * Autoconf 2.64 or later * Automake 1.14 or later +* GNU Autoconf Archive (autoconf-archive on Debian) Your distro may package cross-compiling toolchains such as the following incomplete list on Debian: gcc-aarch64-linux-gnu, gcc-arm-linux-gnueabihf, From d07ebd11d6cf0219f4cbdf0f268f1b082abefdda Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Thu, 20 Nov 2025 23:29:41 -0600 Subject: [PATCH 183/208] tests: Fix nonnative tests labeled as native The tests asn1_test and tpm2_key_protector_test should be labelled as nonnative tests because they run tests on the target. A clue that indicates a nonnative test is the usage of the grub-shell script. Signed-off-by: Glenn Washburn Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- Makefile.util.def | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.util.def b/Makefile.util.def index 7b91c0b61..313dd0a68 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -1294,13 +1294,13 @@ script = { }; script = { - testcase = native; + testcase = nonnative; name = asn1_test; common = tests/asn1_test.in; }; script = { - testcase = native; + testcase = nonnative; name = tpm2_key_protector_test; common = tests/tpm2_key_protector_test.in; }; From 641646376b81593be9490864d56b25cd9922f630 Mon Sep 17 00:00:00 2001 From: George Hu Date: Wed, 26 Nov 2025 16:45:46 +0800 Subject: [PATCH 184/208] lib/x86_64/setjmp: Use 32-bit zero idiom for shorter encoding Switch from "xorq %rax, %rax" to "xorl %eax, %eax". In 64-bit mode zeroing EAX implicitly clears RAX and the 32-bit form encodes are one byte smaller while keeping identical semantics. Signed-off-by: George Hu Reviewed-by: Daniel Kiper --- grub-core/lib/x86_64/setjmp.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/lib/x86_64/setjmp.S b/grub-core/lib/x86_64/setjmp.S index 6b151bc4d..4c6c81249 100644 --- a/grub-core/lib/x86_64/setjmp.S +++ b/grub-core/lib/x86_64/setjmp.S @@ -36,7 +36,7 @@ GRUB_MOD_LICENSE "GPLv3+" */ FUNCTION(grub_setjmp) pop %rsi /* Return address, and adjust the stack */ - xorq %rax, %rax + xorl %eax, %eax movq %rbx, 0(%rdi) /* RBX */ movq %rsp, 8(%rdi) /* RSP */ push %rsi From 09c512b8fabdbc19d5698925c796c43def240bc0 Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Thu, 27 Nov 2025 14:32:12 +0530 Subject: [PATCH 185/208] efiemu/loadcore: Add grub_calloc() failure check Add a failure check after grub_calloc() call. If grub_calloc() fails, e.g., due to memory allocation failure, it returns NULL. Then using grub_efiemu_elfsyms, which will be NULL, later will result in a NULL pointer dereference. Signed-off-by: Avnish Chouhan Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/efiemu/loadcore.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/efiemu/loadcore.c b/grub-core/efiemu/loadcore.c index 2b924623f..4a091bee0 100644 --- a/grub-core/efiemu/loadcore.c +++ b/grub-core/efiemu/loadcore.c @@ -203,6 +203,9 @@ grub_efiemu_count_symbols (const Elf_Ehdr *e) grub_efiemu_elfsyms = (struct grub_efiemu_elf_sym *) grub_calloc (grub_efiemu_nelfsyms, sizeof (struct grub_efiemu_elf_sym)); + if (grub_efiemu_elfsyms == NULL) + return grub_errno; + /* Relocators */ for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); i < e->e_shnum; From 02cae1a3579496ea3fce774d72a4e0f72b721ef1 Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Mon, 1 Dec 2025 15:11:23 +0530 Subject: [PATCH 186/208] disk/ieee1275/ofdisk: Fix memory leaks In case of an overflow "p" and "p->grub_devpath" will not be freed. Fix both issues. Signed-off-by: Avnish Chouhan Reviewed-by: Alec Brown Reviewed-by: Daniel Kiper Reviewed-by: Sudhakar Kuppusamy --- grub-core/disk/ieee1275/ofdisk.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/disk/ieee1275/ofdisk.c b/grub-core/disk/ieee1275/ofdisk.c index dbc0f1aba..f4624a4ad 100644 --- a/grub-core/disk/ieee1275/ofdisk.c +++ b/grub-core/disk/ieee1275/ofdisk.c @@ -93,6 +93,7 @@ ofdisk_hash_add_real (char *devpath) grub_add (sz, sizeof ("ieee1275/"), &sz)) { grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of device path")); + grub_free (p); return NULL; } @@ -109,6 +110,8 @@ ofdisk_hash_add_real (char *devpath) if (grub_add (grub_strlen (p->devpath), 3, &sz)) { grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of an open path")); + grub_free (p->grub_devpath); + grub_free (p); return NULL; } From c0669af6a86b57283087cdea2244332eb892f21c Mon Sep 17 00:00:00 2001 From: Sridhar Markonda Date: Tue, 2 Dec 2025 12:38:45 +0530 Subject: [PATCH 187/208] script/execute: Add a NULL check after grub_calloc() call ... in gettext_append() to handle allocation errors. This prevents NULL pointer dereference and stops crashes during string translation. Signed-off-by: Sridhar Markonda Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/script/execute.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c index 62afcdc99..369d985ee 100644 --- a/grub-core/script/execute.c +++ b/grub-core/script/execute.c @@ -562,6 +562,8 @@ gettext_append (struct grub_script_argv *result, const char *orig_str) if (*iptr == '$') dollar_cnt++; ctx.allowed_strings = grub_calloc (dollar_cnt, sizeof (ctx.allowed_strings[0])); + if (ctx.allowed_strings == NULL) + goto fail; if (parse_string (orig_str, gettext_save_allow, &ctx, 0)) goto fail; From 9b2c8ae5d22463974916fa476f419a5809bc3f6b Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Wed, 10 Dec 2025 23:47:53 +0000 Subject: [PATCH 188/208] commands/bli: Set UINT32_MAX in LoaderTpm2ActivePcrBanks if TPM2 present but no banks protocol The implementation in sd-boot was changed to return UINT32_MAX when the EFI environment detects a working TPM2, but with an older firmware that doesn't implement the protocol to get the list of active banks. This allows distinguishing with the case where there is no working TPM2, in which case userspace just gives up, and instead lets userspace try to figure it out later. Fixes: f326c5c47 (commands/bli: Set LoaderTpm2ActivePcrBanks runtime variable) Signed-off-by: Luca Boccassi Reviewed-by: Daniel Kiper --- grub-core/commands/efi/tpm.c | 65 ++++++++++++++---------------------- 1 file changed, 25 insertions(+), 40 deletions(-) diff --git a/grub-core/commands/efi/tpm.c b/grub-core/commands/efi/tpm.c index 7b493c890..96b509031 100644 --- a/grub-core/commands/efi/tpm.c +++ b/grub-core/commands/efi/tpm.c @@ -39,7 +39,7 @@ static grub_uint8_t grub_tpm_version; static grub_int8_t tpm1_present = -1; static grub_int8_t tpm2_present = -1; -static grub_int8_t tpm2_pcr_banks_reporting_present = -1; +static grub_efi_int64_t tpm2_active_pcr_banks = -1; static grub_efi_boolean_t grub_tpm1_present (grub_efi_tpm_protocol_t *tpm) @@ -90,34 +90,6 @@ grub_tpm2_present (grub_efi_tpm2_protocol_t *tpm) return (grub_efi_boolean_t) tpm2_present; } -static grub_efi_boolean_t -grub_tpm2_pcr_banks_reporting_present (grub_efi_tpm2_protocol_t *tpm) -{ - grub_efi_status_t status; - EFI_TCG2_BOOT_SERVICE_CAPABILITY caps; - - caps.Size = (grub_uint8_t) sizeof (caps); - - if (tpm2_pcr_banks_reporting_present != -1) - return (grub_efi_boolean_t) tpm2_pcr_banks_reporting_present; - - if (!grub_tpm2_present (tpm)) - return (grub_efi_boolean_t) (tpm2_pcr_banks_reporting_present = 0); - - status = tpm->get_capability (tpm, &caps); - - if (status != GRUB_EFI_SUCCESS || caps.StructureVersion.Major < 1 - || (caps.StructureVersion.Major == 1 && caps.StructureVersion.Minor < 1)) - tpm2_pcr_banks_reporting_present = 0; - else - tpm2_pcr_banks_reporting_present = 1; - - grub_dprintf ("tpm", "tpm2 PCR banks reporting%s present\n", - tpm2_pcr_banks_reporting_present ? "" : " NOT"); - - return (grub_efi_boolean_t) tpm2_pcr_banks_reporting_present; -} - static grub_efi_boolean_t grub_tpm_handle_find (grub_efi_handle_t *tpm_handle, grub_efi_uint8_t *protocol_version) @@ -365,32 +337,45 @@ grub_tpm_present (void) grub_uint32_t grub_tpm2_active_pcr_banks (void) { + EFI_TCG2_BOOT_SERVICE_CAPABILITY caps; grub_efi_handle_t tpm_handle; grub_efi_uint8_t protocol_version; grub_efi_tpm2_protocol_t *tpm; - grub_efi_uint32_t active_pcr_banks = 0; + grub_efi_uint32_t active_pcr_banks; + grub_efi_status_t status; + + if (tpm2_active_pcr_banks >= 0) + return (grub_uint32_t) tpm2_active_pcr_banks; if (!grub_tpm_handle_find (&tpm_handle, &protocol_version)) - return 0; + return (grub_uint32_t) (tpm2_active_pcr_banks = 0); if (protocol_version == 1) - return 0; /* We report TPM2 status */ + return (grub_uint32_t) (tpm2_active_pcr_banks = 0); /* We report TPM2 status. */ tpm = grub_efi_open_protocol (tpm_handle, &tpm2_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (tpm == NULL) { grub_dprintf ("tpm", "Cannot open TPM2 protocol\n"); - return 0; + return (grub_uint32_t) (tpm2_active_pcr_banks = 0); } - if (grub_tpm2_pcr_banks_reporting_present (tpm)) - { - grub_efi_status_t status = tpm->get_active_pcr_banks (tpm, &active_pcr_banks); + if (!grub_tpm2_present (tpm)) + return (grub_uint32_t) (tpm2_active_pcr_banks = 0); - if (status != GRUB_EFI_SUCCESS) - return 0; /* Assume none available if the call fails. */ - } + caps.Size = (grub_uint8_t) sizeof (caps); + status = tpm->get_capability (tpm, &caps); + if (status != GRUB_EFI_SUCCESS) + return (grub_uint32_t) (tpm2_active_pcr_banks = 0); + if (caps.StructureVersion.Major < 1 || + (caps.StructureVersion.Major == 1 && caps.StructureVersion.Minor < 1)) + /* There's a working TPM2 but without querying protocol, let userspace figure it out. */ + return (grub_uint32_t) (tpm2_active_pcr_banks = GRUB_UINT_MAX); - return active_pcr_banks; + status = tpm->get_active_pcr_banks (tpm, &active_pcr_banks); + if (status != GRUB_EFI_SUCCESS) + return (grub_uint32_t) (tpm2_active_pcr_banks = 0); /* Assume none available if the call fails. */ + + return (grub_uint32_t) (tpm2_active_pcr_banks = active_pcr_banks); } From ee283b14aea03aff13b95f976f58304124f504d6 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Fri, 12 Dec 2025 00:23:49 -0600 Subject: [PATCH 189/208] tests/util/grub-fs-tester: Use CSMACINTOSH encoding instead of macroman From Debian 12 to 13, recode had a major overhaul and now does not support the macroman encoding. Its unclear if this is a bug or intentional. Regardless, use the CSMACINTOSH encoding instead as MacRoman and it are aliases and CSMACINTOSH is supported on both Debian 12 and 13. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/util/grub-fs-tester.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in index 7199322c6..00ecec015 100644 --- a/tests/util/grub-fs-tester.in +++ b/tests/util/grub-fs-tester.in @@ -743,7 +743,9 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do dd if=/dev/urandom of="${MOUNTDEVICE}" bs=1 seek=$((0x468)) conv=notrunc count=8 MOUNTFS="hfsplus";; x"hfs") - "mkfs.hfs" -b $BLKSIZE -v "`echo $FSLABEL |recode utf8..macroman`" -h "${MOUNTDEVICE}" + # CSMACINTOSH is an alias for MacRoman which is the + # encoding used on HFS. + "mkfs.hfs" -b $BLKSIZE -v "`echo $FSLABEL | recode utf8..CSMACINTOSH`" -h "${MOUNTDEVICE}" dd if=/dev/urandom of="${MOUNTDEVICE}" bs=1 seek=$((0x474)) conv=notrunc count=8 MOUNTOPTS="iocharset=utf8,codepage=macroman," ;; From f551d3de2448ead507a6af6d17afeb34103c2c99 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 13 Dec 2025 04:33:52 +0100 Subject: [PATCH 190/208] commands/efi/lsefisystab: Recognize EFI_MEMORY_ATTRIBUTES_TABLE_GUID and EFI_TCG2_FINAL_EVENTS_TABLE_GUID Let the lsefisystab command recognize the following table GUIDs: - EFI_MEMORY_ATTRIBUTES_TABLE_GUID, - EFI_TCG2_FINAL_EVENTS_TABLE_GUID. Signed-off-by: Heinrich Schuchardt Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/commands/efi/lsefisystab.c | 2 ++ include/grub/efi/api.h | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/grub-core/commands/efi/lsefisystab.c b/grub-core/commands/efi/lsefisystab.c index ffb24fc3b..015d36fe0 100644 --- a/grub-core/commands/efi/lsefisystab.c +++ b/grub-core/commands/efi/lsefisystab.c @@ -47,6 +47,7 @@ static const struct guid_mapping guid_mappings[] = { GRUB_EFI_HOB_LIST_GUID, "HOB LIST"}, { GRUB_EFI_IMAGE_SECURITY_DATABASE_GUID, "IMAGE EXECUTION INFORMATION"}, { GRUB_EFI_LZMA_CUSTOM_DECOMPRESS_GUID, "LZMA CUSTOM DECOMPRESS"}, + { GRUB_EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMORY ATTRIBUTES TABLE"}, { GRUB_EFI_MEMORY_TYPE_INFORMATION_GUID, "MEMORY TYPE INFO"}, { GRUB_EFI_MPS_TABLE_GUID, "MPS"}, { GRUB_EFI_RT_PROPERTIES_TABLE_GUID, "RT PROPERTIES"}, @@ -54,6 +55,7 @@ static const struct guid_mapping guid_mappings[] = { GRUB_EFI_SMBIOS_TABLE_GUID, "SMBIOS"}, { GRUB_EFI_SMBIOS3_TABLE_GUID, "SMBIOS3"}, { GRUB_EFI_SYSTEM_RESOURCE_TABLE_GUID, "SYSTEM RESOURCE TABLE"}, + { GRUB_EFI_TCG2_FINAL_EVENTS_TABLE_GUID, "TCG2 FINAL EVENTS TABLE"}, { GRUB_EFI_TIANO_CUSTOM_DECOMPRESS_GUID, "TIANO CUSTOM DECOMPRESS"}, { GRUB_EFI_TSC_FREQUENCY_GUID, "TSC FREQUENCY"}, }; diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index 9ae908729..f7e9c46a5 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -389,6 +389,16 @@ { 0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89 } \ } +#define GRUB_EFI_MEMORY_ATTRIBUTES_TABLE_GUID \ + { 0xdcfa911d, 0x26eb, 0x469f, \ + { 0xa2, 0x20, 0x38, 0xb7, 0xdc, 0x46, 0x12, 0x20 } \ + } + +#define GRUB_EFI_TCG2_FINAL_EVENTS_TABLE_GUID \ + { 0x1e2ed096, 0x30e2, 0x4254, \ + { 0xbd, 0x89, 0x86, 0x3b, 0xbe, 0xf8, 0x23, 0x25 } \ + } + struct grub_efi_sal_system_table { grub_uint32_t signature; From c477a9551903c76466b9208ccf09dc6b9923dc7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=2E=20H=C3=BCttel?= Date: Sat, 13 Dec 2025 20:59:58 +0100 Subject: [PATCH 191/208] util/grub-install: Allow recursive copying of theme dirs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit grub-install allows to pass a parameter to install a theme in the boot partition. This works fine for the default starfield theme. However, in general themes can contain subdirectories, as, e.g. "icons", and these are not copied by grub-install. As a result, the icons are missing on the screen. Fix this by simple recursive copying. Signed-off-by: Andreas K. Hüttel Reviewed-by: Daniel Kiper --- util/grub-install-common.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/util/grub-install-common.c b/util/grub-install-common.c index a913ee61c..c1c3c5704 100644 --- a/util/grub-install-common.c +++ b/util/grub-install-common.c @@ -803,13 +803,20 @@ copy_all (const char *srcd, || strcmp (de->d_name, "..") == 0) continue; srcf = grub_util_path_concat (2, srcd, de->d_name); - if (grub_util_is_special_file (srcf) - || grub_util_is_directory (srcf)) + if (grub_util_is_special_file (srcf)) { free (srcf); continue; } dstf = grub_util_path_concat (2, dstd, de->d_name); + if (grub_util_is_directory (srcf)) + { + grub_install_mkdir_p (dstf); + copy_all (srcf, dstf); + free (srcf); + free (dstf); + continue; + } grub_install_compress_file (srcf, dstf, 1); free (srcf); free (dstf); From 0f0899c32985d3ab194f01c367f094cb38bcd55c Mon Sep 17 00:00:00 2001 From: Khalid Ali Date: Mon, 15 Dec 2025 16:59:46 +0000 Subject: [PATCH 192/208] efi: Fix several memory leaks of UEFI handles Fix possible and absolute memory leaks of "handles" returned by grub_efi_locate_handle() using grub_malloc(). Signed-off-by: Khalid Ali Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/commands/efi/lsefi.c | 1 + grub-core/commands/efi/tpm.c | 2 ++ grub-core/term/efi/serial.c | 4 ++-- grub-core/video/efi_gop.c | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/grub-core/commands/efi/lsefi.c b/grub-core/commands/efi/lsefi.c index 7b8316d41..f4c10392d 100644 --- a/grub-core/commands/efi/lsefi.c +++ b/grub-core/commands/efi/lsefi.c @@ -129,6 +129,7 @@ grub_cmd_lsefi (grub_command_t cmd __attribute__ ((unused)), } + grub_free (handles); return 0; } diff --git a/grub-core/commands/efi/tpm.c b/grub-core/commands/efi/tpm.c index 96b509031..1c4906403 100644 --- a/grub-core/commands/efi/tpm.c +++ b/grub-core/commands/efi/tpm.c @@ -113,6 +113,7 @@ grub_tpm_handle_find (grub_efi_handle_t *tpm_handle, grub_tpm_version = 1; *protocol_version = 1; grub_dprintf ("tpm", "TPM handle Found, version: 1\n"); + grub_free (handles); return 1; } @@ -125,6 +126,7 @@ grub_tpm_handle_find (grub_efi_handle_t *tpm_handle, grub_tpm_version = 2; *protocol_version = 2; grub_dprintf ("tpm", "TPM handle Found, version: 2\n"); + grub_free (handles); return 1; } diff --git a/grub-core/term/efi/serial.c b/grub-core/term/efi/serial.c index 5dfd2d86c..e409b8d5e 100644 --- a/grub-core/term/efi/serial.c +++ b/grub-core/term/efi/serial.c @@ -169,13 +169,13 @@ grub_efiserial_init (void) port = grub_zalloc (sizeof (*port)); if (!port) - return; + break; port->name = grub_malloc (sizeof ("efiXXXXXXXXXXXXXXXXXXXX")); if (!port->name) { grub_free (port); - return; + break; } grub_snprintf (port->name, sizeof ("efiXXXXXXXXXXXXXXXXXXXX"), "efi%d", num_serial++); diff --git a/grub-core/video/efi_gop.c b/grub-core/video/efi_gop.c index 9452f5e58..1ad2e709d 100644 --- a/grub-core/video/efi_gop.c +++ b/grub-core/video/efi_gop.c @@ -94,7 +94,7 @@ check_protocol (void) gop_handle = 0; grub_dprintf ("video", "GOP: no usable mode\n"); - + grub_free (handles); return 0; } From b733d9d6dcf04c6aba88a4690a916c2cbb4ec5ac Mon Sep 17 00:00:00 2001 From: Radoslav Kolev Date: Fri, 19 Dec 2025 16:24:59 +0200 Subject: [PATCH 193/208] blsuki: Fix typo in entry parameter description Change "specificUKII entries" to "specific UKI entries". Signed-off-by: Radoslav Kolev Reviewed-by: Daniel Kiper --- grub-core/commands/blsuki.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c index a169e3f59..3e4fa0bb3 100644 --- a/grub-core/commands/blsuki.c +++ b/grub-core/commands/blsuki.c @@ -88,7 +88,7 @@ static const struct grub_arg_option uki_opt[] = {"enable-fallback", 'f', 0, "Fallback to the default BLS path if --path fails to find UKI entries.", 0, ARG_TYPE_NONE}, {"show-default", 'd', 0, N_("Allow the default UKI entry to be added to the GRUB menu."), 0, ARG_TYPE_NONE}, {"show-non-default", 'n', 0, N_("Allow the non-default UKI entries to be added to the GRUB menu."), 0, ARG_TYPE_NONE}, - {"entry", 'e', 0, N_("Allow specificUKII entries to be added to the GRUB menu."), N_("FILE"), ARG_TYPE_FILE}, + {"entry", 'e', 0, N_("Allow specific UKI entries to be added to the GRUB menu."), N_("FILE"), ARG_TYPE_FILE}, {0, 0, 0, 0, 0, 0} }; #endif From 242816e93fdfd9d5b767118c2de6c8adf64f3818 Mon Sep 17 00:00:00 2001 From: Radoslav Kolev Date: Fri, 19 Dec 2025 16:25:00 +0200 Subject: [PATCH 194/208] blsuki: Fix position of DIR parameter in blscfg command summary The DIR parameter in the example should be specified after the -p|--path option instead of after -f|fallback. Signed-off-by: Radoslav Kolev Reviewed-by: Daniel Kiper --- grub-core/commands/blsuki.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c index 3e4fa0bb3..c107f4bb3 100644 --- a/grub-core/commands/blsuki.c +++ b/grub-core/commands/blsuki.c @@ -1480,7 +1480,7 @@ static grub_extcmd_t uki_cmd; GRUB_MOD_INIT(blsuki) { bls_cmd = grub_register_extcmd ("blscfg", grub_cmd_blscfg, 0, - N_("[-p|--path] [-f|--enable-fallback] DIR [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"), + N_("[-p|--path] DIR [-f|--enable-fallback] [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"), N_("Import Boot Loader Specification snippets."), bls_opt); #ifdef GRUB_MACHINE_EFI From d19a74a17fd92361921a1e8903321d4eac56bcb4 Mon Sep 17 00:00:00 2001 From: Radoslav Kolev Date: Fri, 19 Dec 2025 16:25:01 +0200 Subject: [PATCH 195/208] blsuki: Use specified device in case of fallback Currently if the fallback option is enabled and no files are found in the specified directory it searches the default (loader/conf) directory but always in the device set by the root environment variable. It makes more sense and also the comment in the code implies, that the default directory on the current device should be searched. Signed-off-by: Radoslav Kolev Reviewed-by: Daniel Kiper --- grub-core/commands/blsuki.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c index c107f4bb3..e8e6aeebc 100644 --- a/grub-core/commands/blsuki.c +++ b/grub-core/commands/blsuki.c @@ -1231,7 +1231,7 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback, enum blsu tmp = blsuki_update_boot_device (default_dir); tmp = grub_stpcpy (tmp, cmd_dir); - blsuki_set_find_entry_info (info, default_dir, NULL, cmd_type); + blsuki_set_find_entry_info (info, default_dir, info->devid, cmd_type); grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n", read_entry_info.dirname, info->dirname); fallback = true; From cfeff5e0718329ad666502c15644fef79625168c Mon Sep 17 00:00:00 2001 From: Radoslav Kolev Date: Fri, 19 Dec 2025 16:25:02 +0200 Subject: [PATCH 196/208] blsuki: Fix default location in comment to /loader/entries Signed-off-by: Radoslav Kolev Reviewed-by: Daniel Kiper --- grub-core/commands/blsuki.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c index e8e6aeebc..bfc4ef6d8 100644 --- a/grub-core/commands/blsuki.c +++ b/grub-core/commands/blsuki.c @@ -1210,7 +1210,7 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback, enum blsu /* * If we aren't able to find BLS entries in the directory given by info->dirname, - * we can fallback to the default location "/boot/loader/entries/" and see if we + * we can fallback to the default location of "/loader/entries/" and see if we * can find the files there. If we can't find UKI entries, fallback to * "/EFI/Linux" on the EFI system partition. */ From 25b7f6b9344a4bac18c26ce143a156ac2bcb3ec4 Mon Sep 17 00:00:00 2001 From: Radoslav Kolev Date: Fri, 19 Dec 2025 16:25:03 +0200 Subject: [PATCH 197/208] blsuki: Error out if unexpected arguments are supplied This can be especially helpful, as the Fedora version of the blscfg actually made use of positional arguments, but current implementation switched to parameters. For example what used to be "blscfg (hd0,gpt2)/..." now should be "blscfg --path (hd0,gpt2)/...)". In case of old configs/scripts still supplying positional arguments we will now error out instead of just ignoring them and falling back to defaults silently. Signed-off-by: Radoslav Kolev Reviewed-by: Daniel Kiper --- grub-core/commands/blsuki.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c index bfc4ef6d8..4133d3111 100644 --- a/grub-core/commands/blsuki.c +++ b/grub-core/commands/blsuki.c @@ -1458,9 +1458,11 @@ blsuki_cmd (grub_extcmd_context_t ctxt, enum blsuki_cmd_type cmd_type) } static grub_err_t -grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), +grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc, char **args __attribute__ ((unused))) { + if (argc != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unexpected argument(s) found, see --help")); return blsuki_cmd (ctxt, BLSUKI_BLS_CMD); } @@ -1468,9 +1470,11 @@ static grub_extcmd_t bls_cmd; #ifdef GRUB_MACHINE_EFI static grub_err_t -grub_cmd_uki (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), +grub_cmd_uki (grub_extcmd_context_t ctxt, int argc, char **args __attribute__ ((unused))) { + if (argc != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unexpected argument(s) found, see --help")); return blsuki_cmd (ctxt, BLSUKI_UKI_CMD); } From 18f08826f98a58f214b622fbd299bf56389cc94d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Bre=C3=9Fler?= Date: Mon, 22 Dec 2025 11:11:05 +0100 Subject: [PATCH 198/208] kern/efi/sb: Enable loading GRUB_FILE_TYPE_CRYPTODISK_ENCRYPTION_KEY and GRUB_FILE_TYPE_CRYPTODISK_DETACHED_HEADER MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ... file types when UEFI Secure Boot is enabled. Otherwise it is not possible to load cryptodisk encryption key or detached header. Fixes: https://savannah.gnu.org/bugs/?65889 Signed-off-by: Ingo Breßler Signed-off-by: Daniel Kiper Reviewed-by: Alec Brown --- grub-core/kern/efi/sb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/kern/efi/sb.c b/grub-core/kern/efi/sb.c index 4409e03c5..b2bf38a54 100644 --- a/grub-core/kern/efi/sb.c +++ b/grub-core/kern/efi/sb.c @@ -152,6 +152,8 @@ shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)), case GRUB_FILE_TYPE_TESTLOAD: case GRUB_FILE_TYPE_GET_SIZE: case GRUB_FILE_TYPE_ZFS_ENCRYPTION_KEY: + case GRUB_FILE_TYPE_CRYPTODISK_ENCRYPTION_KEY: + case GRUB_FILE_TYPE_CRYPTODISK_DETACHED_HEADER: case GRUB_FILE_TYPE_CAT: case GRUB_FILE_TYPE_HEXCAT: case GRUB_FILE_TYPE_CMP: From caaf50b9affbd4c9e7313a4fdc03c4fa439a6a1f Mon Sep 17 00:00:00 2001 From: Srish Srinivasan Date: Mon, 8 Dec 2025 15:51:29 +0530 Subject: [PATCH 199/208] osdep/aros/hostdisk: Fix use-after-free bug during MsgPort deletion ... in function grub_util_fd_open() when creation of an I/O request or opening a device fails. The "ret", the file descriptor, will be freed before its associated MsgPort is deleted resulting in a use-after-free condition. Fix this issue by freeing "ret" after its associated MsgPort has been deleted. Signed-off-by: Srish Srinivasan Reviewed-by: Avnish Chouhan Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- grub-core/osdep/aros/hostdisk.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grub-core/osdep/aros/hostdisk.c b/grub-core/osdep/aros/hostdisk.c index 08723bd45..c75474933 100644 --- a/grub-core/osdep/aros/hostdisk.c +++ b/grub-core/osdep/aros/hostdisk.c @@ -207,8 +207,8 @@ grub_util_fd_open (const char *dev, int flg) sizeof(struct IOExtTD)); if (!ret->ioreq) { - free (ret); DeleteMsgPort (ret->mp); + free (ret); return NULL; } @@ -225,9 +225,9 @@ grub_util_fd_open (const char *dev, int flg) if (OpenDevice ((unsigned char *) tmp, unit, (struct IORequest *) ret->ioreq, flags)) { - free (tmp); - free (ret); DeleteMsgPort (ret->mp); + free (ret); + free (tmp); return NULL; } free (tmp); From 51ebc6f677e7216c8f5a77dd204b2961f27a7c93 Mon Sep 17 00:00:00 2001 From: Srish Srinivasan Date: Fri, 2 Jan 2026 15:57:31 +0530 Subject: [PATCH 200/208] tests: Add functional tests for ecb/cbc helpers Test the following helper functions using AES with 128, 192, and 256 bit keys: - grub_crypto_ecb_encrypt(), - grub_crypto_ecb_decrypt(), - grub_crypto_cbc_encrypt(), - grub_crypto_cbc_decrypt(). Signed-off-by: Srish Srinivasan Reviewed-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper Reviewed-by: Glenn Washburn --- docs/grub.texi | 5 + grub-core/Makefile.core.def | 5 + grub-core/tests/crypto_cipher_mode_test.c | 197 +++++++++++++++++++ grub-core/tests/crypto_cipher_mode_vectors.h | 135 +++++++++++++ grub-core/tests/lib/functional_test.c | 1 + 5 files changed, 343 insertions(+) create mode 100644 grub-core/tests/crypto_cipher_mode_test.c create mode 100644 grub-core/tests/crypto_cipher_mode_vectors.h diff --git a/docs/grub.texi b/docs/grub.texi index 7181009b6..c948e1ee7 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -4078,6 +4078,7 @@ Modules can be loaded via the @command{insmod} (@pxref{insmod}) command. * cpio_be_module:: * cpuid_module:: * crc64_module:: +* crypto_cipher_mode_test_module:: * crypto_module:: * cryptodisk_module:: * cs5536_module:: @@ -4610,6 +4611,10 @@ various CPU features. @xref{cpuid} for more information. @section crc64 This module provides support for the CRC64 operation. +@node crypto_cipher_mode_test_module +@section crypto_cipher_mode_test +This module performs various cipher mode encryption/decryption tests + @node crypto_module @section crypto This module provides library support for various base cryptography operations diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index fa4bc54aa..0cf155128 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2272,6 +2272,11 @@ module = { common = tests/argon2_test.c; }; +module = { + name = crypto_cipher_mode_test; + common = tests/crypto_cipher_mode_test.c; +}; + module = { name = legacy_password_test; common = tests/legacy_password_test.c; diff --git a/grub-core/tests/crypto_cipher_mode_test.c b/grub-core/tests/crypto_cipher_mode_test.c new file mode 100644 index 000000000..f0ebcf377 --- /dev/null +++ b/grub-core/tests/crypto_cipher_mode_test.c @@ -0,0 +1,197 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2025 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + +#include "crypto_cipher_mode_vectors.h" + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Perform cipher lookup, handle init, and key setting. */ +static grub_crypto_cipher_handle_t +handle_init (struct vector vec, grub_crypto_cipher_handle_t handle) +{ + gcry_err_code_t err; + + const gcry_cipher_spec_t *cipher = grub_crypto_lookup_cipher_by_name (vec.cipher); + grub_test_assert (cipher != NULL, "\n%s: cipher lookup failed for %s", vec.mode, vec.cipher); + if (cipher == NULL) + return NULL; + + handle = grub_crypto_cipher_open (cipher); + grub_test_assert (handle != NULL, "\n%s: handle init failed for %s", vec.mode, vec.cipher); + if (handle == NULL) + return NULL; + + err = grub_crypto_cipher_set_key (handle, (grub_uint8_t *) vec.key, vec.keylen); + grub_test_assert (err == GPG_ERR_NO_ERROR, "\n%s: key set of size %d failed for %s with err = %d", + vec.mode, vec.keylen, vec.cipher, err); + if (err != GPG_ERR_NO_ERROR) + { + grub_crypto_cipher_close (handle); + return NULL; + } + + return handle; +} + +static void +ecb_test (struct vector vec) +{ + gcry_err_code_t gcry_err; + grub_crypto_cipher_handle_t handle = NULL; + grub_uint8_t *plaintext = NULL, *ciphertext = NULL; + grub_int32_t rc; + + handle = handle_init (vec, handle); + if (handle == NULL) + return; + + /* Test encryption. */ + ciphertext = grub_zalloc (vec.plen); + grub_test_assert (ciphertext != NULL, "\necb: ciphertext buffer allocation failed"); + if (ciphertext == NULL) + goto out_handle; + + gcry_err = grub_crypto_ecb_encrypt (handle, ciphertext, vec.ptext, vec.plen); + grub_test_assert (gcry_err == GPG_ERR_NO_ERROR, "\necb: encryption failed with err = %d", + gcry_err); + if (gcry_err != GPG_ERR_NO_ERROR) + goto out_ct; + + rc = grub_memcmp (ciphertext, vec.ctext, vec.plen); + grub_test_assert (rc == 0, "\necb: ciphertext mismatch after encryption"); + if (rc != 0) + goto out_ct; + + /* Test decryption. */ + plaintext = grub_zalloc (vec.plen); + grub_test_assert (plaintext != NULL, "\necb: plaintext buffer allocation failed"); + if (plaintext == NULL) + goto out_ct; + + gcry_err = grub_crypto_ecb_decrypt (handle, plaintext, ciphertext, vec.plen); + grub_test_assert (gcry_err == GPG_ERR_NO_ERROR, "\necb: decryption failed failed with err = %d", + gcry_err); + if (gcry_err != GPG_ERR_NO_ERROR) + goto out_pt; + + rc = grub_memcmp (plaintext, vec.ptext, vec.plen); + grub_test_assert (rc == 0, "\necb: plaintext mismatch after decryption"); + + out_pt: + grub_free(plaintext); + out_ct: + grub_free(ciphertext); + out_handle: + grub_crypto_cipher_close(handle); +} + +static void +cbc_test (struct vector vec) +{ + gcry_err_code_t gcry_err; + grub_crypto_cipher_handle_t handle = NULL; + grub_uint8_t *plaintext = NULL, *ciphertext = NULL; + grub_uint32_t *iv = NULL; + grub_int32_t rc; + + handle = handle_init (vec, handle); + if (handle == NULL) + return; + + /* Test Encryption */ + iv = grub_malloc(vec.ivlen); + grub_test_assert (iv != NULL, "\ncbc: IV buffer allocation failed"); + if (iv == NULL) + goto out_handle; + + grub_memcpy (iv, vec.iv_in, vec.ivlen); + + ciphertext = grub_zalloc (vec.plen); + grub_test_assert (ciphertext != NULL, "\ncbc: ciphertext buffer allocation failed"); + if (ciphertext == NULL) + goto out_iv; + + gcry_err = grub_crypto_cbc_encrypt (handle, ciphertext, vec.ptext, vec.plen, iv); + grub_test_assert (gcry_err == GPG_ERR_NO_ERROR, "\ncbc: encryption failed with err = %d", + gcry_err); + if (gcry_err != GPG_ERR_NO_ERROR) + goto out_ct; + + rc = grub_memcmp (ciphertext, vec.ctext, vec.plen); + grub_test_assert (rc == 0, "\ncbc: ciphertext mismatch after encryption"); + if (rc != 0) + goto out_ct; + + rc = grub_memcmp (iv, vec.iv_out, vec.ivlen); + grub_test_assert (rc == 0, "\ncbc: IV out mismatch after encryption"); + if (rc != 0) + goto out_ct; + + /* Test Decryption */ + grub_memcpy (iv, vec.iv_in, vec.ivlen); + + plaintext = grub_zalloc (vec.plen); + grub_test_assert (plaintext != NULL, "\ncbc: plaintext buffer allocation failed"); + if (plaintext == NULL) + goto out_ct; + + gcry_err = grub_crypto_cbc_decrypt (handle, plaintext, ciphertext, vec.plen, iv); + grub_test_assert (gcry_err == GPG_ERR_NO_ERROR, "\ncbc: decryption failed with err = %d", + gcry_err); + if (gcry_err != GPG_ERR_NO_ERROR) + goto out_pt; + + rc = grub_memcmp (plaintext, vec.ptext, vec.plen); + grub_test_assert (rc == 0, "\ncbc: plaintext mismatch after decryption"); + + out_pt: + grub_free(plaintext); + out_ct: + grub_free(ciphertext); + out_iv: + grub_free(iv); + out_handle: + grub_crypto_cipher_close(handle); +} + +static void +crypto_cipher_mode_test (void) +{ + grub_size_t i; + + for (i = 0; i < ARRAY_SIZE (vecs); i++) + { + if (grub_strcmp (vecs[i].mode, "ecb") == 0) + ecb_test(vecs[i]); + else if (grub_strcmp (vecs[i].mode, "cbc") == 0) + cbc_test(vecs[i]); + else + { + grub_test_assert(0, "\n%s mode unsupported for testing", vecs[i].mode); + return; + } + } +} + +/* Register example_test method as a functional test. */ +GRUB_FUNCTIONAL_TEST (crypto_cipher_mode_test, crypto_cipher_mode_test); diff --git a/grub-core/tests/crypto_cipher_mode_vectors.h b/grub-core/tests/crypto_cipher_mode_vectors.h new file mode 100644 index 000000000..8ef948b46 --- /dev/null +++ b/grub-core/tests/crypto_cipher_mode_vectors.h @@ -0,0 +1,135 @@ +struct vector +{ + const char *cipher; + const char *mode; + const char *key; + grub_uint32_t keylen; + const char *ptext; + grub_uint32_t plen; + const char *ctext; + const char *iv_in; + const char *iv_out; + grub_uint32_t ivlen; +} vecs[] = { + { + .cipher = "aes", + .mode = "ecb", + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .keylen = 16, + .ptext = "\x00\x11\x22\x33\x44\x55\x66\x77" + "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", + .plen = 16, + .ctext = "\x69\xc4\xe0\xd8\x6a\x7b\x04\x30" + "\xd8\xcd\xb7\x80\x70\xb4\xc5\x5a", + }, + { + .cipher = "aes", + .mode = "ecb", + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17", + .keylen = 24, + .ptext = "\x00\x11\x22\x33\x44\x55\x66\x77" + "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", + .plen = 16, + .ctext = "\xdd\xa9\x7c\xa4\x86\x4c\xdf\xe0" + "\x6e\xaf\x70\xa0\xec\x0d\x71\x91", + }, + { + .cipher = "aes", + .mode = "ecb", + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .keylen = 32, + .ptext = "\x00\x11\x22\x33\x44\x55\x66\x77" + "\x88\x99\xaa\xbb\xcc\xdd\xee\xff", + .plen = 16, + .ctext = "\x8e\xa2\xb7\xca\x51\x67\x45\xbf" + "\xea\xfc\x49\x90\x4b\x49\x60\x89", + }, + { + .cipher = "aes", + .mode = "cbc", + .key = "\xc2\x86\x69\x6d\x88\x7c\x9a\xa0" + "\x61\x1b\xbb\x3e\x20\x25\xa4\x5a", + .keylen = 16, + .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .plen = 32, + .ctext = "\xd2\x96\xcd\x94\xc2\xcc\xcf\x8a" + "\x3a\x86\x30\x28\xb5\xe1\xdc\x0a" + "\x75\x86\x60\x2d\x25\x3c\xff\xf9" + "\x1b\x82\x66\xbe\xa6\xd6\x1a\xb1", + .iv_in = "\x56\x2e\x17\x99\x6d\x09\x3d\x28" + "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58", + .iv_out = "\x75\x86\x60\x2d\x25\x3c\xff\xf9" + "\x1b\x82\x66\xbe\xa6\xd6\x1a\xb1", + .ivlen = 16, + }, + { + .cipher = "aes", + .mode = "cbc", + .key = "\x8e\x73\xb0\xf7\xda\x0e\x64\x52" + "\xc8\x10\xf3\x2b\x80\x90\x79\xe5" + "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", + .keylen = 24, + .ptext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" + "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57\x1e\x03\xac\x9c" + "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" + "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11" + "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" + "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17" + "\xad\x2b\x41\x7b\xe6\x6c\x37\x10", + .plen = 64, + .ctext = "\x4f\x02\x1d\xb2\x43\xbc\x63\x3d" + "\x71\x78\x18\x3a\x9f\xa0\x71\xe8" + "\xb4\xd9\xad\xa9\xad\x7d\xed\xf4" + "\xe5\xe7\x38\x76\x3f\x69\x14\x5a" + "\x57\x1b\x24\x20\x12\xfb\x7a\xe0" + "\x7f\xa9\xba\xac\x3d\xf1\x02\xe0" + "\x08\xb0\xe2\x79\x88\x59\x88\x81" + "\xd9\x20\xa9\xe6\x4f\x56\x15\xcd", + .iv_in = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .iv_out = "\x08\xb0\xe2\x79\x88\x59\x88\x81" + "\xd9\x20\xa9\xe6\x4f\x56\x15\xcd", + .ivlen = 16, + }, + { + .cipher = "aes", + .mode = "cbc", + .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe" + "\x2b\x73\xae\xf0\x85\x7d\x77\x81" + "\x1f\x35\x2c\x07\x3b\x61\x08\xd7" + "\x2d\x98\x10\xa3\x09\x14\xdf\xf4", + .keylen = 32, + .ptext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" + "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57\x1e\x03\xac\x9c" + "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" + "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11" + "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" + "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17" + "\xad\x2b\x41\x7b\xe6\x6c\x37\x10", + .plen = 64, + .ctext = "\xf5\x8c\x4c\x04\xd6\xe5\xf1\xba" + "\x77\x9e\xab\xfb\x5f\x7b\xfb\xd6" + "\x9c\xfc\x4e\x96\x7e\xdb\x80\x8d" + "\x67\x9f\x77\x7b\xc6\x70\x2c\x7d" + "\x39\xf2\x33\x69\xa9\xd9\xba\xcf" + "\xa5\x30\xe2\x63\x04\x23\x14\x61" + "\xb2\xeb\x05\xe2\xc3\x9b\xe9\xfc" + "\xda\x6c\x19\x07\x8c\x6a\x9d\x1b", + .iv_in = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .iv_out = "\xb2\xeb\x05\xe2\xc3\x9b\xe9\xfc" + "\xda\x6c\x19\x07\x8c\x6a\x9d\x1b", + .ivlen = 16, + }, +}; diff --git a/grub-core/tests/lib/functional_test.c b/grub-core/tests/lib/functional_test.c index 776934e1f..4214332df 100644 --- a/grub-core/tests/lib/functional_test.c +++ b/grub-core/tests/lib/functional_test.c @@ -82,6 +82,7 @@ grub_functional_all_tests (grub_extcmd_context_t ctxt __attribute__ ((unused)), grub_dl_load ("shift_test"); grub_dl_load ("asn1_test"); grub_dl_load ("argon2_test"); + grub_dl_load ("crypto_cipher_mode_test"); FOR_LIST_ELEMENTS (test, grub_test_list) ok = !grub_test_run (test) && ok; From ff1edd97526baa7a4aa247966612f46b6c28f14e Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Fri, 2 Jan 2026 16:13:13 +0530 Subject: [PATCH 201/208] util/grub-mkimagexx: Stop generating unaligned appended signatures When creating the core image with an unaligned appended signature size, e.g. 479, for PowerPC, the grub-mkimage aligns the appended signature size to a multiple of 4 bytes, but it does not add a padding needed to align to multiple of 4 bytes appended signature size in the appended signature ELF note. Therefore, after signing and installing this core image, the firmware tries to read the magic string "~Module signature appended~" from the appended signature ELF note but gets the partial magic string like "Module signature appended~". It leads to the appended signature magic string match failure. Example: grub-mkimage -O powerpc-ieee1275 -o core.elf -p /grub -x \ kernel.der --appended-signature-size 479 ... sign-file SHA256 ./grub.key ./grub.pem ./core.elf ./core.elf.signed Without padding: hexdump -C ./core.elf.signed ... 00383550 00 00 00 13 00 00 01 e0 41 53 69 67 41 70 70 65 |........ASigAppe| 00383560 6e 64 65 64 2d 53 69 67 6e 61 74 75 72 65 00 00 |nded-Signature..| ... 003836f0 dd 47 cd ed 02 8e 15 af 5b 09 2e 44 6f da 67 88 |.G......[..Do.g.| 00383700 4d 94 17 31 26 9d 47 95 d8 7c ad 36 00 d2 9c 53 |M..1&.G..|.6...S| 00383710 20 e0 af 60 78 cd 22 e6 ed 45 1e b1 e7 7e cf b5 | ..`x."..E...~..| 00383720 fc 58 ec df 1b ab 7a 00 00 02 00 00 00 00 00 00 |.X....z.........| 00383730 00 01 b7 7e 4d 6f 64 75 6c 65 20 73 69 67 6e 61 |...~Module signa| 00383740 74 75 72 65 20 61 70 70 65 6e 64 65 64 7e 0a |ture appended~.| Fix this by adding a padding required to align appended signature size in the appended signature ELF note to multiple of 4 bytes. Example: grub-mkimage -O powerpc-ieee1275 -o core.elf -p /grub -x \ kernel.der --appended-signature-size 479 ... sign-file SHA256 ./grub.key ./grub.pem ./core.elf ./core.elf.signed With padding: hexdump -C ./core.elf.signed ... 00137460 62 00 00 00 00 00 00 13 00 00 01 ec 41 53 69 67 |b...........ASig| 00137470 41 70 70 65 6e 64 65 64 2d 53 69 67 6e 61 74 75 |Appended-Signatu| ... 00137610 b7 07 cd b6 c8 ca 9a 5b 7c 13 8c 75 1d 1c 54 81 |.......[|..u..T.| 00137620 7f c4 9a 8b bd d7 73 8d 2f 7d d2 e6 d1 3c 52 a9 |......s./}... Reviewed-by: Daniel Kiper --- util/grub-mkimagexx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c index 7fe2e35e6..1a453a5e8 100644 --- a/util/grub-mkimagexx.c +++ b/util/grub-mkimagexx.c @@ -249,6 +249,7 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc { phnum++; footer_size += ALIGN_UP (sizeof (struct grub_appended_signature_note), 4); + footer_size += ALIGN_UP_OVERHEAD (appsig_size, 4); } if (image_target->id != IMAGE_LOONGSON_ELF) From 1aa0dd0c04a7e66d127915c54d7d7f1de5974d4f Mon Sep 17 00:00:00 2001 From: Yao Zi Date: Wed, 31 Dec 2025 09:05:53 +0000 Subject: [PATCH 202/208] configure: Defer check for -mcmodel=large until PIC/PIE checks are done On RISC-V, large code model is only compatible with position-depedent code. However, the configure script checks availability of -mcmodel=large before determining whether PIC/PIE is enabled, and disable them. This is problematic with toolchains that enable PIE by default, where check for -mcmodel=large will always fail with, cc1: sorry, unimplemented: code model 'large' with '-fPIC' and -mcmodel=medany will be silently used instead, causing relocation failures at runtime with some memory layouts since -mcmodel=medany requires all data and code to stay within a contiguous 4 GiB range. Let's defer the check for -mcmodel=large until PIC/PIE is ensured disabled. Fixes: f1957dc8a334 (RISC-V: Add to build system) Reported-by: Han Gao Signed-off-by: Yao Zi Reviewed-by: Daniel Kiper --- configure.ac | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/configure.ac b/configure.ac index a282bf7bf..a833c7612 100644 --- a/configure.ac +++ b/configure.ac @@ -1543,21 +1543,6 @@ AC_SUBST(TARGET_LDFLAGS_OLDMAGIC) LDFLAGS="$TARGET_LDFLAGS" -if test "$target_cpu" = x86_64 || test "$target_cpu" = sparc64 || test "$target_cpu" = riscv64 ; then - # Use large model to support 4G memory - AC_CACHE_CHECK([whether option -mcmodel=large works], grub_cv_cc_mcmodel, [ - CFLAGS="$TARGET_CFLAGS -mcmodel=large" - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], - [grub_cv_cc_mcmodel=yes], - [grub_cv_cc_mcmodel=no]) - ]) - if test "x$grub_cv_cc_mcmodel" = xyes; then - TARGET_CFLAGS="$TARGET_CFLAGS -mcmodel=large" - elif test "$target_cpu" = sparc64 || test "$target_cpu" = riscv64; then - TARGET_CFLAGS="$TARGET_CFLAGS -mcmodel=medany" - fi -fi - if test "$target_cpu"-"$platform" = x86_64-efi; then # EFI writes to stack below %rsp, we must not use the red zone AC_CACHE_CHECK([whether option -mno-red-zone works], grub_cv_cc_no_red_zone, [ @@ -1666,6 +1651,21 @@ fi] CFLAGS="$TARGET_CFLAGS" +if test "$target_cpu" = x86_64 || test "$target_cpu" = sparc64 || test "$target_cpu" = riscv64 ; then + # Use large model to support 4G memory + AC_CACHE_CHECK([whether option -mcmodel=large works], grub_cv_cc_mcmodel, [ + CFLAGS="$TARGET_CFLAGS -mcmodel=large" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_mcmodel=yes], + [grub_cv_cc_mcmodel=no]) + ]) + if test "x$grub_cv_cc_mcmodel" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -mcmodel=large" + elif test "$target_cpu" = sparc64 || test "$target_cpu" = riscv64; then + TARGET_CFLAGS="$TARGET_CFLAGS -mcmodel=medany" + fi +fi + # Stack smashing protector. grub_CHECK_STACK_PROTECTOR AC_ARG_ENABLE([stack-protector], From f94eae0f8de428fd25a4c923662cd36ed552b486 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Wed, 24 Dec 2025 17:58:59 +0530 Subject: [PATCH 203/208] grub-mkimage: Do not generate empty SBAT metadata When creating core.elf with SBAT the grub-mkimage does not check if an SBAT metadata file contains at least an SBAT header or not. It leads to adding an empty SBAT ELF note for PowerPC and the .sbat section for EFI. Fix this by checking the SBAT metadata file size against the SBAT header size before adding SBAT contents to the ELF note or .sbat section. Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- util/mkimage.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/util/mkimage.c b/util/mkimage.c index f364a5718..2920c0249 100644 --- a/util/mkimage.c +++ b/util/mkimage.c @@ -56,6 +56,9 @@ #pragma GCC diagnostic ignored "-Wcast-align" +#define SBAT_HEADER "sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md" +#define SBAT_HEADER_SIZE (sizeof (SBAT_HEADER)) + #define TARGET_NO_FIELD 0xffffffff /* use 2015-01-01T00:00:00+0000 as a stock timestamp */ @@ -963,6 +966,12 @@ grub_install_generate_image (const char *dir, const char *prefix, if (sbat_path != NULL && (image_target->id != IMAGE_EFI && image_target->id != IMAGE_PPC)) grub_util_error (_("SBAT data can be added only to EFI or powerpc-ieee1275 images")); + else if (sbat_path != NULL) + { + sbat_size = grub_util_get_image_size (sbat_path); + if (sbat_size < SBAT_HEADER_SIZE) + grub_util_error (_("%s file should contain at least an SBAT header"), sbat_path); + } if (appsig_size != 0 && image_target->id != IMAGE_PPC) grub_util_error (_("appended signature can be support only to powerpc-ieee1275 images")); @@ -1396,7 +1405,7 @@ grub_install_generate_image (const char *dir, const char *prefix, if (sbat_path != NULL) { - sbat_size = ALIGN_ADDR (grub_util_get_image_size (sbat_path)); + sbat_size = ALIGN_ADDR (sbat_size); sbat_size = ALIGN_UP (sbat_size, GRUB_PE32_FILE_ALIGNMENT); } @@ -1857,7 +1866,6 @@ grub_install_generate_image (const char *dir, const char *prefix, char *sbat = NULL; if (sbat_path != NULL) { - sbat_size = grub_util_get_image_size (sbat_path); sbat = xmalloc (sbat_size); grub_util_load_image (sbat_path, sbat); layout.sbat_size = sbat_size; From e37d0215830b1221c7a0c62f4642716b142f8f5c Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Mon, 12 Jan 2026 23:20:14 +0530 Subject: [PATCH 204/208] kern/ieee1275/openfw: Add a check for invalid partition number The grub_strtoul() may fail in several scenarios like invalid input, overflow, etc. Lack of proper check may lead to unexpected failures in the code further. Signed-off-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/kern/ieee1275/openfw.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/grub-core/kern/ieee1275/openfw.c b/grub-core/kern/ieee1275/openfw.c index 3b492dd96..06757f5d8 100644 --- a/grub-core/kern/ieee1275/openfw.c +++ b/grub-core/kern/ieee1275/openfw.c @@ -512,7 +512,20 @@ grub_ieee1275_encode_devname (const char *path) } if (partition && partition[0]) { - unsigned int partno = grub_strtoul (partition, 0, 0); + unsigned long partno; + const char *endptr; + + partno = grub_strtoul (partition, &endptr, 0); + grub_errno = GRUB_ERR_NONE; + if (*endptr != '\0' || partno > 65535 || + (partno == 0 && ! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_0_BASED_PARTITIONS))) + { + grub_free (partition); + grub_free (device); + grub_free (encoding); + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid partition number")); + return NULL; + } *optr++ = ','; @@ -520,7 +533,7 @@ grub_ieee1275_encode_devname (const char *path) /* GRUB partition 1 is OF partition 0. */ partno++; - grub_snprintf (optr, sizeof ("XXXXXXXXXXXX"), "%d", partno); + grub_snprintf (optr, sizeof ("XXXXXXXXXXXX"), "%lu", partno); } else *optr = '\0'; From ac042f3f58d33ce9cd5ff61750f06da1a1d7b0eb Mon Sep 17 00:00:00 2001 From: Radoslav Kolev Date: Tue, 13 Jan 2026 10:04:22 +0200 Subject: [PATCH 205/208] configure: Print a more helpful error if autoconf-archive is not installed ... because an undefined macro receives another macro as parameter and autoconf is not smart enough to produce a useful error message. Signed-off-by: Radoslav Kolev Reviewed-by: Daniel Kiper --- configure.ac | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/configure.ac b/configure.ac index a833c7612..066fb74d4 100644 --- a/configure.ac +++ b/configure.ac @@ -1792,6 +1792,11 @@ LIBS="" # Defined in acinclude.m4. grub_ASM_USCORE grub_PROG_TARGET_CC + +# The error message produced by autoconf if autoconf-archive is not installed is +# quite misleading and not very helpful. So, try point people in the right direction. +m4_ifndef([AX_CHECK_LINK_FLAG], [m4_fatal([autoconf-archive is missing. You must install it to generate the configure script.])]) + if test "x$TARGET_APPLE_LINKER" != x1 ; then AX_CHECK_LINK_FLAG([-Wl,--image-base,0x400000], [TARGET_IMG_BASE_LDOPT="-Wl,--image-base"], From 35bfd6c47864e5fc0d51415e8874b2efd567b96d Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Tue, 13 Jan 2026 18:38:38 +0100 Subject: [PATCH 206/208] build: Add grub-core/tests/crypto_cipher_mode_vectors.h file to EXTRA_DIST This file was not added to EXTRA_DIST during test creation. Fixes: 51ebc6f67 (tests: Add functional tests for ecb/cbc helpers) Signed-off-by: Daniel Kiper Reviewed-by: Alec Brown Reviewed-by: Srish Srinivasan Reviewed-by: Sudhakar Kuppusamy --- conf/Makefile.extra-dist | 1 + 1 file changed, 1 insertion(+) diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index 288ba1f87..d22b6c862 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -72,6 +72,7 @@ EXTRA_DIST += grub-core/lib/libtasn1 EXTRA_DIST += $(shell find $(top_srcdir)/include -name '*.h') EXTRA_DIST += $(shell find $(top_srcdir)/grub-core/lib -name '*.h') EXTRA_DIST += grub-core/efiemu/runtime/config.h +EXTRA_DIST += grub-core/tests/crypto_cipher_mode_vectors.h EXTRA_DIST += grub-core/tests/asn1/asn1_test.h EXTRA_DIST += $(shell find $(top_srcdir)/grub-core/tests/asn1/tests -name '*.h') EXTRA_DIST += $(shell find $(top_srcdir)/grub-core/commands/tpm2_key_protector -name '*.h') From d38d6a1a9b79427848976f53d474392cd29c2a71 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Wed, 14 Jan 2026 16:46:57 +0100 Subject: [PATCH 207/208] Release 2.14 Signed-off-by: Daniel Kiper --- NEWS | 23 +++++++++++++++++++++++ configure.ac | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 310130962..13a8f490f 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,26 @@ +New in 2.14: + +* libgcrypt 1.11. +* LVM LV integrity and cachevol support. +* EROFS support. +* GRUB environment block inside the Btrfs header support. +* NX support for EFI platforms. +* shim loader protocol support. +* BLS and UKI support. +* Argon2 KDF support. +* TPM2 key protector support. +* Appended Signature Secure Boot Support for PowerPC. +* New option to block command line interface. +* Support dates outside of 1901..2038 range. +* zstdio decompression support. +* EFI code improvements and fixes. +* TPM driver fixes. +* Filesystems fixes. +* CVE and Coverity fixes. +* Tests improvements. +* Documentation improvements. +* ... and tons of other fixes and cleanups... + New in 2.12: * GCC 13 support. diff --git a/configure.ac b/configure.ac index 066fb74d4..47a96c36f 100644 --- a/configure.ac +++ b/configure.ac @@ -34,7 +34,7 @@ dnl "TARGET_" (such as TARGET_CC, TARGET_CFLAGS, etc.) are used for dnl the target type. See INSTALL for full list of variables and dnl description of the relationships between them. -AC_INIT([GRUB],[2.14~rc1],[bug-grub@gnu.org]) +AC_INIT([GRUB],[2.14],[bug-grub@gnu.org]) AS_CASE(["$ERROR_PLATFORM_NOT_SUPPORT_SSP"], [n | no | nO | N | No | NO], [ERROR_PLATFORM_NOT_SUPPORT_SSP=no], From eaa3b8f0f90605a82c6bfda4c5c4b73c58eb81ac Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Wed, 14 Jan 2026 20:05:13 +0100 Subject: [PATCH 208/208] Bump version to 2.15 Signed-off-by: Daniel Kiper --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 47a96c36f..d8ca1b7c1 100644 --- a/configure.ac +++ b/configure.ac @@ -34,7 +34,7 @@ dnl "TARGET_" (such as TARGET_CC, TARGET_CFLAGS, etc.) are used for dnl the target type. See INSTALL for full list of variables and dnl description of the relationships between them. -AC_INIT([GRUB],[2.14],[bug-grub@gnu.org]) +AC_INIT([GRUB],[2.15],[bug-grub@gnu.org]) AS_CASE(["$ERROR_PLATFORM_NOT_SUPPORT_SSP"], [n | no | nO | N | No | NO], [ERROR_PLATFORM_NOT_SUPPORT_SSP=no],